mirror of
https://github.com/muesli/telephant
synced 2024-11-22 19:33:06 +00:00
Use OAuth to authorize with Mastodon API
This commit is contained in:
parent
c4f5332b31
commit
fe0ac8c5eb
6 changed files with 210 additions and 36 deletions
|
@ -20,13 +20,8 @@ const (
|
|||
|
||||
// Account is a Mastodon account for Chirp.
|
||||
type Account struct {
|
||||
username string
|
||||
password string
|
||||
instance string
|
||||
clientID string
|
||||
clientSecret string
|
||||
|
||||
client *mastodon.Client
|
||||
config *mastodon.Config
|
||||
self *mastodon.Account
|
||||
|
||||
evchan chan interface{}
|
||||
|
@ -34,30 +29,54 @@ type Account struct {
|
|||
}
|
||||
|
||||
// NewAccount returns a new Mastodon account.
|
||||
func NewAccount(username, password, instance, clientID, clientSecret string) *Account {
|
||||
return &Account{
|
||||
username: username,
|
||||
password: password,
|
||||
instance: instance,
|
||||
clientID: clientID,
|
||||
clientSecret: clientSecret,
|
||||
func NewAccount(instance, token, clientID, clientSecret string) *Account {
|
||||
mconfig := &mastodon.Config{
|
||||
Server: instance,
|
||||
AccessToken: token,
|
||||
ClientID: clientID,
|
||||
ClientSecret: clientSecret,
|
||||
}
|
||||
|
||||
return &Account{
|
||||
config: mconfig,
|
||||
client: mastodon.NewClient(mconfig),
|
||||
}
|
||||
}
|
||||
|
||||
func RegisterAccount(instance string) (*Account, string, string, error) {
|
||||
app, err := mastodon.RegisterApp(context.Background(), &mastodon.AppConfig{
|
||||
Server: instance,
|
||||
ClientName: "Telephant",
|
||||
Scopes: "read write follow post",
|
||||
Website: "",
|
||||
})
|
||||
if err != nil {
|
||||
return nil, "", "", err
|
||||
}
|
||||
|
||||
a := NewAccount(instance, "", app.ClientID, app.ClientSecret)
|
||||
|
||||
return a, app.AuthURI, app.RedirectURI, nil
|
||||
}
|
||||
|
||||
func (mod *Account) Authenticate(code string) (string, string, string, string, error) {
|
||||
err := mod.client.AuthenticateToken(context.Background(), code, "urn:ietf:wg:oauth:2.0:oob")
|
||||
if err != nil {
|
||||
return "", "", "", "", err
|
||||
}
|
||||
|
||||
return mod.config.Server, mod.config.AccessToken, mod.config.ClientID, mod.config.ClientSecret, nil
|
||||
}
|
||||
|
||||
// Run executes the account's event loop.
|
||||
func (mod *Account) Run(eventChan chan interface{}) {
|
||||
mod.evchan = eventChan
|
||||
|
||||
mod.client = mastodon.NewClient(&mastodon.Config{
|
||||
Server: mod.instance,
|
||||
ClientID: mod.clientID,
|
||||
ClientSecret: mod.clientSecret,
|
||||
})
|
||||
err := mod.client.Authenticate(context.Background(), mod.username, mod.password)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
if mod.config.AccessToken == "" {
|
||||
return
|
||||
}
|
||||
|
||||
var err error
|
||||
mod.self, err = mod.client.GetAccountCurrentUser(context.Background())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -75,6 +94,10 @@ func (mod *Account) Run(eventChan chan interface{}) {
|
|||
mod.handleStream()
|
||||
}
|
||||
|
||||
func (mod *Account) Logo() string {
|
||||
return "mastodon.svg"
|
||||
}
|
||||
|
||||
// Post posts a new status
|
||||
func (mod *Account) Post(message string) error {
|
||||
_, err := mod.client.PostStatus(context.Background(), &mastodon.Toot{
|
||||
|
|
|
@ -9,6 +9,9 @@ import (
|
|||
type UIBridge struct {
|
||||
core.QObject
|
||||
|
||||
_ func(instance string) `slot:"connectButton"`
|
||||
_ func(instance string) `slot:"authButton"`
|
||||
|
||||
_ func(replyid string, message string) `slot:"postButton"`
|
||||
_ func(id string) `slot:"shareButton"`
|
||||
_ func(id string) `slot:"likeButton"`
|
||||
|
@ -52,6 +55,8 @@ func setupQmlBridges() {
|
|||
accountBridge.SetUsername("Chirp!")
|
||||
|
||||
uiBridge = NewUIBridge(nil)
|
||||
uiBridge.ConnectConnectButton(connectToInstance)
|
||||
uiBridge.ConnectAuthButton(authInstance)
|
||||
uiBridge.ConnectPostButton(reply)
|
||||
uiBridge.ConnectShareButton(share)
|
||||
uiBridge.ConnectLikeButton(like)
|
||||
|
|
34
chirp.go
34
chirp.go
|
@ -1,6 +1,7 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
|
@ -12,6 +13,35 @@ import (
|
|||
"github.com/muesli/chirp/accounts/mastodon"
|
||||
)
|
||||
|
||||
var (
|
||||
config Config
|
||||
)
|
||||
|
||||
func connectToInstance(instance string) {
|
||||
var authURI string
|
||||
var redirectURI string
|
||||
var err error
|
||||
tc, authURI, redirectURI, err = mastodon.RegisterAccount(instance)
|
||||
|
||||
fmt.Println("auth uri:", authURI)
|
||||
// fmt.Println("redirect uri:", redirectURI)
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
func authInstance(code string) {
|
||||
instance, token, clientID, clientSecret, err := tc.Authenticate(code)
|
||||
fmt.Println("authenticate:", err)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
config.Account[0].Instance = instance
|
||||
config.Account[0].ClientID = clientID
|
||||
config.Account[0].ClientSecret = clientSecret
|
||||
config.Account[0].Token = token
|
||||
setupMastodon(config.Account[0])
|
||||
}
|
||||
|
||||
// reply is used to post a new message
|
||||
// if replyid is > 0, it's send as a reply
|
||||
func reply(replyid string, message string) {
|
||||
|
@ -59,7 +89,7 @@ func runApp(config Config) {
|
|||
|
||||
// setupMastodon starts a new Mastodon client and sets up event handling & models for it
|
||||
func setupMastodon(config Account) {
|
||||
tc = mastodon.NewAccount(config.Username, config.Password, config.Instance, config.ClientID, config.ClientSecret)
|
||||
tc = mastodon.NewAccount(config.Instance, config.Token, config.ClientID, config.ClientSecret)
|
||||
postModel := NewMessageModel(nil)
|
||||
notificationModel := NewMessageModel(nil)
|
||||
|
||||
|
@ -83,7 +113,7 @@ func main() {
|
|||
setupQmlBridges()
|
||||
|
||||
// load config
|
||||
config := LoadConfig()
|
||||
config = LoadConfig()
|
||||
if config.Style == "" {
|
||||
config.Style = "Material"
|
||||
}
|
||||
|
|
18
config.go
18
config.go
|
@ -16,10 +16,10 @@ type Account struct {
|
|||
AccessTokenSecret string
|
||||
*/
|
||||
Instance string
|
||||
Username string
|
||||
Password string
|
||||
ClientID string
|
||||
ClientSecret string
|
||||
Token string
|
||||
RedirectURI string
|
||||
}
|
||||
|
||||
// Config holds chirp's config settings
|
||||
|
@ -37,18 +37,10 @@ func LoadConfig() Config {
|
|||
_, err := os.Stat(configFile)
|
||||
if err != nil {
|
||||
SaveConfig(Config{
|
||||
Style: "Material",
|
||||
Account: []Account{
|
||||
{
|
||||
Instance: "your instance",
|
||||
Username: "your username",
|
||||
Password: "your password",
|
||||
ClientID: "your client id",
|
||||
ClientSecret: "your client secret",
|
||||
},
|
||||
},
|
||||
Style: "Material",
|
||||
Account: []Account{Account{}},
|
||||
})
|
||||
log.Fatal("Config file is missing, but a template was created for you! Please edit ", configFile)
|
||||
//log.Fatal("Config file is missing, but a template was created for you! Please edit ", configFile)
|
||||
}
|
||||
|
||||
var config Config
|
||||
|
|
116
qml/ConnectDialog.qml
Normal file
116
qml/ConnectDialog.qml
Normal file
|
@ -0,0 +1,116 @@
|
|||
import QtQuick 2.4
|
||||
import QtQuick.Controls 2.1
|
||||
import QtQuick.Controls.Material 2.1
|
||||
import QtQuick.Layouts 1.3
|
||||
|
||||
Popup {
|
||||
id: connectDialog
|
||||
property string instance
|
||||
|
||||
modal: true
|
||||
focus: true
|
||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 16
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
|
||||
Label {
|
||||
text: qsTr("Add an Account")
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
font.bold: true
|
||||
}
|
||||
|
||||
Image {
|
||||
id: logo
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
smooth: true
|
||||
source: "images/accounts/mastodon.svg"
|
||||
sourceSize.height: 128
|
||||
}
|
||||
|
||||
SwipeView {
|
||||
id: connectSwipeView
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Component.onCompleted: contentItem.interactive = false
|
||||
|
||||
currentIndex: 0
|
||||
Item {
|
||||
id: instancePage
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
TextArea {
|
||||
id: instanceArea
|
||||
focus: true
|
||||
selectByMouse: true
|
||||
placeholderText: qsTr("Instance, e.g. https://mastodon.social")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Button {
|
||||
id: connectButton
|
||||
enabled: instanceArea.text.length > 0
|
||||
Layout.alignment: Qt.AlignBottom | Qt.AlignCenter
|
||||
highlighted: true
|
||||
Material.accent: Material.Blue
|
||||
text: qsTr("Authorize Telephant")
|
||||
|
||||
onClicked: {
|
||||
var instance = instanceArea.text
|
||||
connectSwipeView.currentIndex = 1
|
||||
uiBridge.connectButton(instance)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
id: authPage
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
|
||||
TextArea {
|
||||
id: codeArea
|
||||
focus: true
|
||||
selectByMouse: true
|
||||
placeholderText: qsTr("Auth code provided by your instance")
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
Button {
|
||||
id: authButton
|
||||
enabled: codeArea.text.length > 0
|
||||
Layout.alignment: Qt.AlignBottom | Qt.AlignCenter
|
||||
highlighted: true
|
||||
Material.accent: Material.Blue
|
||||
text: qsTr("Login")
|
||||
|
||||
onClicked: {
|
||||
var code = codeArea.text
|
||||
connectDialog.close()
|
||||
uiBridge.authButton(code)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PageIndicator {
|
||||
id: indicator
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
|
||||
count: connectSwipeView.count
|
||||
currentIndex: connectSwipeView.currentIndex
|
||||
|
||||
// anchors.bottom: connectSwipeView.bottom
|
||||
// anchors.horizontalCenter: parent.horizontalCenter
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -29,6 +29,14 @@ ApplicationWindow {
|
|||
width: Math.min(mainWindow.width, mainWindow.height) / 3 * 2
|
||||
}
|
||||
|
||||
ConnectDialog {
|
||||
id: connectDialog
|
||||
x: mainWindow.width / 2 - width / 2
|
||||
y: mainWindow.height / 2 - height / 2 - mainWindow.header.height / 2
|
||||
width: 340
|
||||
height: 340
|
||||
}
|
||||
|
||||
SettingsDialog {
|
||||
id: settingsDialog
|
||||
x: (mainWindow.width - width) / 2
|
||||
|
|
Loading…
Reference in a new issue