Extremely basic functionality, posting

This commit is contained in:
2025-11-22 09:15:52 +00:00
parent c3ebbe6e7b
commit 01776f0ec0

101
main.go
View File

@@ -3,58 +3,61 @@ package main
import ( import (
"fyne.io/fyne/v2" "fyne.io/fyne/v2"
"fyne.io/fyne/v2/app" "fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/widget" "fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog" "fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/layout" "fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/container" "fyne.io/fyne/v2/widget"
"github.com/google/uuid"
"github.com/kirsle/configdir" "github.com/kirsle/configdir"
"github.com/mattn/go-mastodon" "github.com/mattn/go-mastodon"
"github.com/google/uuid"
webview "github.com/webview/webview_go" webview "github.com/webview/webview_go"
"encoding/json" "encoding/json"
"path/filepath" "path/filepath"
"context"
"errors" "errors"
"fmt" "fmt"
"log" "log"
"os" "os"
"context" "strings"
) )
var App fyne.App var App fyne.App
var MainWindow fyne.Window var MainWindow fyne.Window
// Client used for posting, getting posts, etc.
var Client *mastodon.Client
// Federale config settings apply to all profiles. // Federale config settings apply to all profiles.
// The config stores the name of the profile to launch, // The config stores the name of the profile to launch,
// as well as if the profile selection screen should // as well as if the profile selection screen should
// show when the program is next launched. // show when the program is next launched.
type FederaleConfig struct { type FederaleConfig struct {
ProfileName string ProfileName string
DoNotDropToProfileSelection bool DoNotDropToProfileSelection bool
} }
type FederaleProfile struct { // Blueprint for a Federale profile type FederaleProfile struct { // Blueprint for a Federale profile
Name string // Name displayed to user Name string // Name displayed to user
InternalName string // Filename InternalName string // Filename
Server string // Homeserver to connect to Server string // Homeserver to connect to
Username string // Username of user (user@domain) Username string // Username of user (user@domain)
ClientID string // ID of the client ClientID string // ID of the client
ClientSecret string // Secret of the client ClientSecret string // Secret of the client
UserAuthorizationCode string // Authorization code of the user UserAuthorizationCode string // Authorization code of the user
Running bool // Whether the profile is currently running Running bool // Whether the profile is currently running
} }
var LoadedProfile *FederaleProfile // Profile currently loaded into memory for this Federale instance var LoadedProfile *FederaleProfile // Profile currently loaded into memory for this Federale instance
var LoadedConfig *FederaleConfig // Config currently loaded into memory var LoadedConfig *FederaleConfig // Config currently loaded into memory
var Profiles []*FederaleProfile // Profiles loaded from FS go here. var Profiles []*FederaleProfile // Profiles loaded from FS go here.
var ProfileSetupDone bool = false var ProfileSetupDone bool = false
var ProfileSetupProfile *FederaleProfile var ProfileSetupProfile *FederaleProfile
@@ -68,7 +71,7 @@ func SaveConfigToDisk() error {
err := configdir.MakePath(ConfigPath) // Ensure it exists. err := configdir.MakePath(ConfigPath) // Ensure it exists.
if err != nil { if err != nil {
return err return err
} }
ConfigFilePath := filepath.Join(ConfigPath, "federale.json") ConfigFilePath := filepath.Join(ConfigPath, "federale.json")
@@ -93,7 +96,7 @@ func ProfileLaunch() {
err := configdir.MakePath(ConfigPath) // Ensure it exists. err := configdir.MakePath(ConfigPath) // Ensure it exists.
if err != nil { if err != nil {
panic(err) panic(err)
} }
Files, err := os.ReadDir(ConfigPath) Files, err := os.ReadDir(ConfigPath)
@@ -139,13 +142,16 @@ func ProfileLaunch() {
AddProfileWindow.SetFixedSize(true) AddProfileWindow.SetFixedSize(true)
GoButton := widget.NewButton("Go", func() { GoButton := widget.NewButton("Go", func() {
if !strings.HasPrefix(InstanceEntry.Text, "https://") {
InstanceEntry.SetText("https://" + InstanceEntry.Text) // FIXME: This may not work with darknet instances?
}
Domain := InstanceEntry.Text Domain := InstanceEntry.Text
// Step one: register the application // Step one: register the application
AppConfig := &mastodon.AppConfig{ AppConfig := &mastodon.AppConfig{
Server: Domain, Server: Domain,
ClientName: "Federalé", ClientName: "Federalé",
Scopes: "read write push", Scopes: "read write push",
Website: "https://forge.sunglocto.net", Website: "https://forge.sunglocto.net",
RedirectURIs: "urn:ietf:wg:oauth:2.0:oob", RedirectURIs: "urn:ietf:wg:oauth:2.0:oob",
} }
@@ -167,10 +173,10 @@ func ProfileLaunch() {
AddProfileWindow.Resize(fyne.NewSize(500, 500)) AddProfileWindow.Resize(fyne.NewSize(500, 500))
// Step three: get the authorization code from the user // Step three: get the authorization code from the user
dialog.ShowConfirm("Confirm", "Do you have an authorisation code?", func (b bool) { dialog.ShowConfirm("Confirm", "Do you have an authorisation code?", func(b bool) {
if b { if b {
NewProfile := new(FederaleProfile) NewProfile := new(FederaleProfile)
NewProfile.Name = fmt.Sprintf("Profile %d", len(Profiles) + 1) NewProfile.Name = fmt.Sprintf("Profile %d", len(Profiles)+1)
NewProfile.InternalName = uuid.New().String() NewProfile.InternalName = uuid.New().String()
NewProfile.Server = Domain NewProfile.Server = Domain
NewProfile.ClientID = app.ClientID NewProfile.ClientID = app.ClientID
@@ -187,14 +193,27 @@ func ProfileLaunch() {
dialog.ShowForm("Enter authorization code", "Continue", "Exit", FormItems, func(b bool) { dialog.ShowForm("Enter authorization code", "Continue", "Exit", FormItems, func(b bool) {
if b { if b {
NewProfile.UserAuthorizationCode = AuthPasswordWidget.Text config := &mastodon.Config{
Server: NewProfile.Server,
ClientID: NewProfile.ClientID,
ClientSecret: NewProfile.ClientSecret,
}
// Create the client
c := mastodon.NewClient(config)
err = c.GetUserAccessToken(context.Background(), AuthPasswordWidget.Text, app.RedirectURI)
if err != nil {
panic(err)
}
NewProfile.UserAuthorizationCode = c.Config.AccessToken
fmt.Println("Successfully created user profile:\n", NewProfile) fmt.Println("Successfully created user profile:\n", NewProfile)
// Save profile to disk // Save profile to disk
b, err := json.MarshalIndent(NewProfile, "", "\t") b, err := json.MarshalIndent(NewProfile, "", "\t")
if err != nil { if err != nil {
panic(err) panic(err)
} }
err = os.WriteFile(filepath.Join(ConfigPath, NewProfile.InternalName + ".json"), b, 0644) err = os.WriteFile(filepath.Join(ConfigPath, NewProfile.InternalName+".json"), b, 0644)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@@ -234,9 +253,6 @@ func ProfileLaunch() {
) )
} }
Box.Add(ProfileSelection) Box.Add(ProfileSelection)
RootBox := container.New(layout.NewCenterLayout(), Box) RootBox := container.New(layout.NewCenterLayout(), Box)
@@ -267,8 +283,6 @@ func ProfileLaunch() {
} }
} }
func main() { func main() {
log.Println("Checking for federale config") log.Println("Checking for federale config")
@@ -278,7 +292,7 @@ func main() {
err := configdir.MakePath(ConfigPath) // Ensure it exists. err := configdir.MakePath(ConfigPath) // Ensure it exists.
log.Println("Creating federale folder if it does not exist") log.Println("Creating federale folder if it does not exist")
if err != nil { if err != nil {
panic(err) panic(err)
} }
log.Println("Checking if configuration file exists") log.Println("Checking if configuration file exists")
@@ -330,23 +344,46 @@ func main() {
return return
} }
ProfilePath := filepath.Join(ConfigPath, LoadedConfig.ProfileName + ".json") ProfilePath := filepath.Join(ConfigPath, LoadedConfig.ProfileName+".json")
log.Println("Reading profile from disk")
b, err = os.ReadFile(ProfilePath) b, err = os.ReadFile(ProfilePath)
if err != nil { if err != nil {
panic(err) panic(err)
} }
tempprof := new(FederaleProfile)
log.Println("Unmarshalling config to RAM") log.Println("Unmarshalling config to RAM")
err = json.Unmarshal(b, LoadedConfig) err = json.Unmarshal(b, tempprof)
if err != nil { if err != nil {
return panic(err)
} }
LoadedProfile = tempprof
//////////////////////////////////////////////////// ////////////////////////////////////////////////////
config := &mastodon.Config{
Server: LoadedProfile.Server,
ClientID: LoadedProfile.ClientID,
ClientSecret: LoadedProfile.ClientSecret,
AccessToken: LoadedProfile.UserAuthorizationCode,
}
Client = mastodon.NewClient(config)
log.Println(Client)
App = app.New() App = app.New()
MainWindow = App.NewWindow("Federalé") MainWindow = App.NewWindow("Federalé")
MainWindow.SetContent(widget.NewLabel("Hello World!")) TootEntry := widget.NewEntry()
MainWindow.SetContent(container.NewVBox(TootEntry, widget.NewButton("Post", func() {
toot := mastodon.Toot{
Status: TootEntry.Text,
Visibility: "public",
}
_, err := Client.PostStatus(context.Background(), &toot)
if err != nil {
dialog.ShowError(err, MainWindow)
}
})))
MainWindow.ShowAndRun() MainWindow.ShowAndRun()
} }