telephant/telephant.go

341 lines
8.5 KiB
Go
Raw Normal View History

2017-08-29 05:09:17 +00:00
package main
import (
"fmt"
2017-08-29 14:05:51 +00:00
"log"
2017-08-29 05:09:17 +00:00
"os"
"strings"
2017-08-29 05:09:17 +00:00
"github.com/therecipe/qt/core"
"github.com/therecipe/qt/gui"
"github.com/therecipe/qt/qml"
"github.com/therecipe/qt/quickcontrols2"
2019-05-09 14:52:56 +00:00
gap "github.com/muesli/go-app-paths"
2019-05-09 14:33:26 +00:00
"github.com/muesli/telephant/accounts/mastodon"
2017-08-29 05:09:17 +00:00
)
var (
config Config
notificationModel = NewMessageModel(nil)
conversationModel = NewMessageModel(nil)
accountMessagesModel = NewMessageModel(nil)
paneModel = NewPaneModel(nil)
)
// connectToInstance registers an app with the instance and retrieves an
// authentication URI.
func connectToInstance(instance string) bool {
var authURI string
var redirectURI string
var err error
instance = addHTTPPrefixIfNeeded(instance)
tc, authURI, redirectURI, err = mastodon.RegisterAccount(instance)
2019-05-10 13:46:39 +00:00
if err != nil {
fmt.Println("Error registering app:", err)
accountBridge.SetError(err.Error())
return false
2019-05-10 13:46:39 +00:00
}
configBridge.SetAuthURL(authURI)
2019-05-10 13:46:39 +00:00
configBridge.SetRedirectURL(redirectURI)
fmt.Println("auth uri:", authURI)
fmt.Println("redirect uri:", redirectURI)
return true
}
// addHTTPPrefixIfNeeded adds "https://" to an instance URL where it's missing.
func addHTTPPrefixIfNeeded(instance string) string {
if !strings.HasPrefix(instance, "http://") && !strings.HasPrefix(instance, "https://") {
return "https://" + instance
}
return instance
}
// authInstance authenticates a user via OAuth and retrieves an accesstoken
// we'll use for future logins.
func authInstance(code, redirectURI string) bool {
2019-05-10 13:46:39 +00:00
instance, token, clientID, clientSecret, err := tc.Authenticate(code, redirectURI)
if err != nil {
2019-05-12 00:39:05 +00:00
fmt.Println("Error authenticating with instance:", err)
accountBridge.SetError(err.Error())
return false
}
config.Account[0].Instance = instance
config.Account[0].ClientID = clientID
config.Account[0].ClientSecret = clientSecret
config.Account[0].Token = token
setupMastodon(config.Account[0])
return true
}
2019-05-02 08:37:04 +00:00
// reply is used to post a new message
2017-08-29 05:09:17 +00:00
// if replyid is > 0, it's send as a reply
func reply(replyid string, message string) {
var err error
if replyid != "" {
log.Println("Sending reply to:", replyid, message)
err = tc.Reply(replyid, message)
2017-08-29 05:09:17 +00:00
} else {
log.Println("Posting:", message)
err = tc.Post(message)
2017-08-29 05:09:17 +00:00
}
if err != nil {
2019-05-12 00:39:05 +00:00
accountBridge.SetError(err.Error())
log.Println("Error posting:", err)
2017-08-29 05:09:17 +00:00
}
}
// deletePost deletes a post
func deletePost(id string) {
log.Println("Deleting:", id)
if err := tc.DeletePost(id); err != nil {
accountBridge.SetError(err.Error())
log.Println("Error deleting:", err)
}
}
2019-05-06 20:51:31 +00:00
// share a post
func share(id string) {
log.Println("Sharing:", id)
if err := tc.Share(id); err != nil {
2019-05-12 00:39:05 +00:00
accountBridge.SetError(err.Error())
log.Println("Error sharing:", err)
}
2017-08-29 05:09:17 +00:00
}
2019-05-06 20:51:31 +00:00
// unshare a post
func unshare(id string) {
log.Println("Unsharing:", id)
if err := tc.Unshare(id); err != nil {
2019-05-12 00:39:05 +00:00
accountBridge.SetError(err.Error())
log.Println("Error unsharing:", err)
2019-05-06 20:51:31 +00:00
}
}
// like a post
2017-08-29 05:09:17 +00:00
func like(id string) {
log.Println("Liking:", id)
if err := tc.Like(id); err != nil {
2019-05-12 00:39:05 +00:00
accountBridge.SetError(err.Error())
log.Println("Error liking:", err)
}
2017-08-29 05:09:17 +00:00
}
2019-05-06 20:51:31 +00:00
// unlike a post
func unlike(id string) {
log.Println("Unliking:", id)
if err := tc.Unlike(id); err != nil {
2019-05-12 00:39:05 +00:00
accountBridge.SetError(err.Error())
log.Println("Error unliking:", err)
2019-05-06 20:51:31 +00:00
}
}
// follow changes the relationship to another user
func follow(id string, follow bool) {
if follow {
log.Println("Following:", id)
if err := tc.Follow(id); err != nil {
2019-05-12 00:39:05 +00:00
accountBridge.SetError(err.Error())
log.Println("Error following user:", err)
return
}
} else {
log.Println("Unfollowing:", id)
if err := tc.Unfollow(id); err != nil {
2019-05-12 00:39:05 +00:00
accountBridge.SetError(err.Error())
log.Println("Error unfollowing user:", err)
return
}
}
profileBridge.SetFollowing(follow)
}
// loadConversation loads a message thread
func loadConversation(id string) {
log.Println("Loading conversation:", id)
messages, err := tc.LoadConversation(id)
if err != nil {
log.Println("Error loading conversation:", err)
return
}
fmt.Println("Found conversation posts:", len(messages))
conversationModel.Clear()
for _, m := range messages {
p := messageFromEvent(m)
conversationModel.AppendMessage(p)
}
}
// loadAccount loads an entire profile
func loadAccount(id string) {
log.Println("Loading account:", id)
profile, messages, err := tc.LoadAccount(id)
if err != nil {
log.Println("Error loading account:", err)
return
}
profileBridge.SetUsername(profile.Username)
profileBridge.SetName(profile.Name)
profileBridge.SetAvatar(profile.Avatar)
profileBridge.SetProfileURL(profile.ProfileURL)
profileBridge.SetProfileID(profile.ProfileID)
profileBridge.SetPosts(profile.Posts)
profileBridge.SetFollowCount(profile.FollowCount)
profileBridge.SetFollowerCount(profile.FollowerCount)
profileBridge.SetFollowing(profile.Following)
profileBridge.SetFollowedBy(profile.FollowedBy)
fmt.Println("Found account posts:", len(messages))
accountMessagesModel.Clear()
for _, m := range messages {
p := messageFromEvent(m)
accountMessagesModel.AppendMessage(p)
}
}
// tag
func tag(token string) {
model := NewMessageModel(nil)
evchan := make(chan interface{})
go handleEvents(evchan, model)
log.Println("Hashtag:", token)
if err := tc.Tag(token, evchan); err != nil {
accountBridge.SetError(err.Error())
log.Println("Error retrieving hashtag:", err)
return
}
var pane = NewPane(nil)
pane.Name = "Tag: #" + token
pane.Model = model
paneModel.AddPane(pane)
}
// closePane closes a pane
func closePane(idx int64) {
fmt.Println("Closing pane", idx)
paneModel.RemovePane(int(idx))
}
2017-08-29 05:09:17 +00:00
// runApp loads and executes the QML UI
func runApp(config Config) {
var theme string
switch config.Theme {
case "System":
theme = ""
case "Light":
theme = "Default"
default:
theme = config.Theme
}
if theme != "" {
quickcontrols2.QQuickStyle_SetStyle(theme)
}
2017-08-29 05:09:17 +00:00
app := qml.NewQQmlApplicationEngine(nil)
app.RootContext().SetContextProperty("uiBridge", uiBridge)
app.RootContext().SetContextProperty("accountBridge", accountBridge)
app.RootContext().SetContextProperty("profileBridge", profileBridge)
2017-08-29 05:09:17 +00:00
app.RootContext().SetContextProperty("settings", configBridge)
2019-05-09 14:33:26 +00:00
app.Load(core.NewQUrl3("qrc:/qml/telephant.qml", 0))
2017-08-29 05:09:17 +00:00
gui.QGuiApplication_Exec()
}
2019-05-01 15:15:56 +00:00
// setupMastodon starts a new Mastodon client and sets up event handling & models for it
func setupMastodon(config Account) {
tc = mastodon.NewAccount(config.Instance, config.Token, config.ClientID, config.ClientSecret)
2019-05-02 08:37:04 +00:00
postModel := NewMessageModel(nil)
accountBridge.SetUsername("Connecting...")
accountBridge.SetNotifications(notificationModel)
accountBridge.SetConversation(conversationModel)
accountBridge.SetAccountMessages(accountMessagesModel)
// Notifications model must the first model to be added
// It will always be displayed right-most
2019-05-15 03:03:15 +00:00
paneModel.clear()
{
var pane = NewPane(nil)
pane.Name = "Notifications"
pane.Sticky = true
pane.Model = notificationModel
paneModel.AddPane(pane)
}
{
var pane = NewPane(nil)
pane.Name = "Messages"
pane.Model = postModel
paneModel.AddPane(pane)
}
2017-08-29 05:09:17 +00:00
panes := tc.Panes()
for _, p := range panes {
model := NewMessageModel(nil)
evchan := make(chan interface{})
go handleEvents(evchan, model)
p.Stream(evchan)
var pane = NewPane(nil)
pane.Name = p.Title
pane.Model = model
paneModel.AddPane(pane)
}
accountBridge.SetPanes(paneModel)
2017-08-29 05:09:17 +00:00
evchan := make(chan interface{})
go handleEvents(evchan, postModel)
2019-05-10 13:47:17 +00:00
go tc.Run(evchan)
2017-08-29 05:09:17 +00:00
}
func main() {
2019-05-09 14:33:26 +00:00
core.QCoreApplication_SetApplicationName("Telephant")
2019-05-01 15:15:56 +00:00
core.QCoreApplication_SetOrganizationName("fribbledom.com")
2017-08-29 05:09:17 +00:00
core.QCoreApplication_SetAttribute(core.Qt__AA_EnableHighDpiScaling, true)
2019-05-11 00:14:35 +00:00
ga := gui.NewQGuiApplication(len(os.Args), os.Args)
ga.SetWindowIcon(gui.NewQIcon5(":/qml/images/telephant_logo.png"))
2017-08-29 05:09:17 +00:00
setupQmlBridges()
// load config
2019-05-09 14:52:56 +00:00
scope := gap.NewScope(gap.User, "fribbledom.com", "telephant")
configDir, err := scope.ConfigPath("")
if err != nil {
panic(err)
}
os.MkdirAll(configDir, 0700)
configFile, err := scope.ConfigPath("telephant.conf")
if err != nil {
panic(err)
}
2019-05-09 14:52:56 +00:00
config = LoadConfig(configFile)
if config.Theme == "" {
config.Theme = "Material"
}
2017-08-29 05:09:17 +00:00
if config.Style == "" {
config.Style = "Dark"
2017-08-29 05:09:17 +00:00
}
configBridge.SetTheme(config.Theme)
2017-08-29 05:09:17 +00:00
configBridge.SetStyle(config.Style)
2019-05-10 13:47:17 +00:00
configBridge.SetFirstRun(config.FirstRun)
2017-08-29 05:09:17 +00:00
2019-05-01 15:15:56 +00:00
setupMastodon(config.Account[0])
2017-08-29 05:09:17 +00:00
runApp(config)
// save config
config.Theme = configBridge.Theme()
2017-08-29 05:09:17 +00:00
config.Style = configBridge.Style()
2019-05-10 13:47:17 +00:00
config.FirstRun = false
2019-05-09 14:52:56 +00:00
SaveConfig(configFile, config)
2017-08-29 05:09:17 +00:00
}