mirror of
https://dev.narayana.im/narayana/telegabber.git
synced 2026-08-05 04:07:07 +00:00
Add post-login initial configuration stages to the login wizard
This commit is contained in:
parent
bb333edf69
commit
f875e679da
7 changed files with 267 additions and 49 deletions
|
|
@ -72,6 +72,25 @@ var ConfigKeys = []string{
|
||||||
"ignoregroupdeletions",
|
"ignoregroupdeletions",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var Presets = map[string][][]string{
|
||||||
|
"modern": [][]string{
|
||||||
|
{"asciiarrows", "false"},
|
||||||
|
{"oobmode", "true"},
|
||||||
|
{"carbons", "true"},
|
||||||
|
{"hideids", "true"},
|
||||||
|
{"receipts", "true"},
|
||||||
|
{"nativeedits", "true"},
|
||||||
|
},
|
||||||
|
"classic": [][]string{
|
||||||
|
{"asciiarrows", "true"},
|
||||||
|
{"oobmode", "false"},
|
||||||
|
{"carbons", "false"},
|
||||||
|
{"hideids", "false"},
|
||||||
|
{"receipts", "false"},
|
||||||
|
{"nativeedits", "false"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
var sessionDB *SessionsYamlDB
|
var sessionDB *SessionsYamlDB
|
||||||
var sessionsLock sync.Mutex
|
var sessionsLock sync.Mutex
|
||||||
|
|
||||||
|
|
@ -369,3 +388,16 @@ func toBool(s string) (bool, error) {
|
||||||
|
|
||||||
return false, errors.New("Invalid boolean value")
|
return false, errors.New("Invalid boolean value")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NormalizeProperty converts typed properties with uncertain values to certain ones
|
||||||
|
func NormalizeProperty(key, value string) string {
|
||||||
|
if PropertyType(key) == PropertyTypeBool {
|
||||||
|
if value == "0" {
|
||||||
|
value = "false"
|
||||||
|
}
|
||||||
|
if value == "1" {
|
||||||
|
value = "true"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,44 @@ type IntPair struct {
|
||||||
MessageId int64
|
MessageId int64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type barrier struct {
|
||||||
|
mu sync.Mutex
|
||||||
|
open bool
|
||||||
|
releaseCh chan struct{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait blocks until the barrier is released.
|
||||||
|
func (b *barrier) Wait() {
|
||||||
|
b.mu.Lock()
|
||||||
|
if !b.open {
|
||||||
|
b.open = true
|
||||||
|
b.releaseCh = make(chan struct{}) // Reinitialize the channel
|
||||||
|
}
|
||||||
|
b.mu.Unlock()
|
||||||
|
|
||||||
|
// Wait for the barrier to be released
|
||||||
|
<-b.releaseCh
|
||||||
|
}
|
||||||
|
|
||||||
|
// Done releases the barrier.
|
||||||
|
func (b *barrier) Done() {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
|
if b.open {
|
||||||
|
close(b.releaseCh) // Close the channel to release waiting goroutines
|
||||||
|
b.open = false // Mark the barrier as closed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsPending checks if the barrier is currently being waited
|
||||||
|
func (b *barrier) IsPending() bool {
|
||||||
|
b.mu.Lock()
|
||||||
|
defer b.mu.Unlock()
|
||||||
|
|
||||||
|
return b.open
|
||||||
|
}
|
||||||
|
|
||||||
// Client stores the metadata for lazily invoked TDlib instance
|
// Client stores the metadata for lazily invoked TDlib instance
|
||||||
type Client struct {
|
type Client struct {
|
||||||
client *client.Client
|
client *client.Client
|
||||||
|
|
@ -71,6 +109,7 @@ type Client struct {
|
||||||
online bool
|
online bool
|
||||||
|
|
||||||
loginWizard *loginWizardMetadata
|
loginWizard *loginWizardMetadata
|
||||||
|
loginStage LoginStage
|
||||||
|
|
||||||
lastAuthorizationStateType string
|
lastAuthorizationStateType string
|
||||||
|
|
||||||
|
|
@ -108,6 +147,7 @@ type clientLocks struct {
|
||||||
pinOutboxLock sync.Mutex
|
pinOutboxLock sync.Mutex
|
||||||
lastMsgHashesLock sync.Mutex
|
lastMsgHashesLock sync.Mutex
|
||||||
lastMsgIdsLock sync.RWMutex
|
lastMsgIdsLock sync.RWMutex
|
||||||
|
loginFinish barrier
|
||||||
|
|
||||||
authorizerReadLock sync.Mutex
|
authorizerReadLock sync.Mutex
|
||||||
authorizerWriteLock sync.Mutex
|
authorizerWriteLock sync.Mutex
|
||||||
|
|
@ -117,7 +157,7 @@ type clientLocks struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type loginWizardMetadata struct {
|
type loginWizardMetadata struct {
|
||||||
nextStage chan string
|
nextStage chan LoginStage
|
||||||
chanBusy bool
|
chanBusy bool
|
||||||
commandSent bool
|
commandSent bool
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,9 @@ var transportCommands = map[string]command{
|
||||||
"join": command{1, []string{"https://t.me/invite_link"}, "join to chat via invite link or @publicname", true, nil},
|
"join": command{1, []string{"https://t.me/invite_link"}, "join to chat via invite link or @publicname", true, nil},
|
||||||
"supergroup": command{1, []string{"title", "description"}, "create new supergroup «title» with «description»", true, nil},
|
"supergroup": command{1, []string{"title", "description"}, "create new supergroup «title» with «description»", true, nil},
|
||||||
"channel": command{1, []string{"title", "description"}, "create new channel «title» with «description»", true, nil},
|
"channel": command{1, []string{"title", "description"}, "create new channel «title» with «description»", true, nil},
|
||||||
|
"preset": command{1, []string{"modern|classic|pass"}, "apply a config preset", false, nil},
|
||||||
|
"pass": command{0, []string{}, "proceed to next login stage", false, nil},
|
||||||
|
"finish": command{0, []string{}, "skip post-login configuration", false, nil},
|
||||||
}
|
}
|
||||||
|
|
||||||
var notForGroups = []ChatType{ChatTypeBasicGroup, ChatTypeSupergroup, ChatTypeChannel}
|
var notForGroups = []ChatType{ChatTypeBasicGroup, ChatTypeSupergroup, ChatTypeChannel}
|
||||||
|
|
@ -346,7 +349,7 @@ func (c *Client) ProcessTransportCommand(cmdline string, resource string) (strin
|
||||||
}
|
}
|
||||||
// sign out
|
// sign out
|
||||||
case "logout":
|
case "logout":
|
||||||
if !c.Online() {
|
if !c.Online() && !c.locks.loginFinish.IsPending() {
|
||||||
return notOnline, false
|
return notOnline, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -360,6 +363,8 @@ func (c *Client) ProcessTransportCommand(cmdline string, resource string) (strin
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Session.Login = ""
|
c.Session.Login = ""
|
||||||
|
c.wizardStageOrPrompt(LoginStageCancel, "")
|
||||||
|
c.online = false
|
||||||
// cancel auth
|
// cancel auth
|
||||||
case "cancelauth":
|
case "cancelauth":
|
||||||
if c.Online() {
|
if c.Online() {
|
||||||
|
|
@ -465,6 +470,9 @@ func (c *Client) ProcessTransportCommand(cmdline string, resource string) (strin
|
||||||
case "false":
|
case "false":
|
||||||
go c.MigrateFromMUCs()
|
go c.MigrateFromMUCs()
|
||||||
}
|
}
|
||||||
|
if c.loginStage == LoginStageMUC {
|
||||||
|
c.wizardStageOrPrompt(LoginStageSuccess, "")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
gateway.DirtySessions = true
|
gateway.DirtySessions = true
|
||||||
|
|
||||||
|
|
@ -519,11 +527,46 @@ func (c *Client) ProcessTransportCommand(cmdline string, resource string) (strin
|
||||||
return c.cmdChannel(args, cmdline)
|
return c.cmdChannel(args, cmdline)
|
||||||
case "help":
|
case "help":
|
||||||
return c.helpString(CommandTypeTransport, 0), true
|
return c.helpString(CommandTypeTransport, 0), true
|
||||||
|
case "preset":
|
||||||
|
cancelNextStage := false
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
if !cancelNextStage {
|
||||||
|
go c.promptMUC()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
switch args[0] {
|
||||||
|
case "modern", "classic":
|
||||||
|
for _, row := range persistence.Presets[args[0]] {
|
||||||
|
_, err := c.Session.Set(row[0], row[1])
|
||||||
|
if err != nil {
|
||||||
|
return err.Error(), false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gateway.DirtySessions = true
|
||||||
|
return fmt.Sprintf("Applied preset %s", args[0]), true
|
||||||
|
default:
|
||||||
|
cancelNextStage = true
|
||||||
|
return "Invalid argument. Allowed ones are " + transportCommands["preset"].Arguments[0], false
|
||||||
|
}
|
||||||
|
case "pass":
|
||||||
|
switch c.loginStage {
|
||||||
|
case LoginStagePreset:
|
||||||
|
go c.promptMUC()
|
||||||
|
case LoginStageMUC:
|
||||||
|
c.wizardStageOrPrompt(LoginStageSuccess, "")
|
||||||
|
}
|
||||||
|
case "finish":
|
||||||
|
c.wizardStageOrPrompt(LoginStageSuccess, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
return "", true
|
return "", true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Client) promptMUC() {
|
||||||
|
c.wizardStageOrPrompt(LoginStageMUC, "Enable MUCs? Telegabber still supports the legacy approach of mapping Telegram group chats to personal messages in XMPP which might be suitable for some cases like logging or reliable participation in all group chats. Use `/config muc {true|false}` or /finish")
|
||||||
|
}
|
||||||
|
|
||||||
// ProcessChatCommand executes a command sent in a mapped chat
|
// ProcessChatCommand executes a command sent in a mapped chat
|
||||||
// and returns a response, the status of command support and the execution success result
|
// and returns a response, the status of command support and the execution success result
|
||||||
func (c *Client) ProcessChatCommand(chatID int64, cmdline string) (string, bool, bool) {
|
func (c *Client) ProcessChatCommand(chatID int64, cmdline string) (string, bool, bool) {
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,7 @@ func (c *Client) Connect(resource string) error {
|
||||||
tdlibClient, err := client.NewClient(c.authorizer, c.options...)
|
tdlibClient, err := client.NewClient(c.authorizer, c.options...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.locks.authorizationReady.Unlock()
|
c.locks.authorizationReady.Unlock()
|
||||||
c.wizardStageOrPrompt("cancel", "")
|
c.wizardStageOrPrompt(LoginStageCancel, "")
|
||||||
return errors.Wrap(err, "Couldn't initialize a Telegram client instance")
|
return errors.Wrap(err, "Couldn't initialize a Telegram client instance")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -138,8 +138,6 @@ func (c *Client) Connect(resource string) error {
|
||||||
// stage 3: if a client is succesfully created, AuthorizationStateReady is already reached
|
// stage 3: if a client is succesfully created, AuthorizationStateReady is already reached
|
||||||
log.Warn("Authorization successful!")
|
log.Warn("Authorization successful!")
|
||||||
|
|
||||||
c.wizardStageOrPrompt("success", "")
|
|
||||||
|
|
||||||
c.me, err = c.client.GetMe()
|
c.me, err = c.client.GetMe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Error("Could not retrieve me info")
|
log.Error("Could not retrieve me info")
|
||||||
|
|
@ -147,6 +145,8 @@ func (c *Client) Connect(resource string) error {
|
||||||
c.Session.Login = c.me.PhoneNumber
|
c.Session.Login = c.me.PhoneNumber
|
||||||
}
|
}
|
||||||
|
|
||||||
|
c.locks.loginFinish.Wait()
|
||||||
|
|
||||||
go c.updateHandler()
|
go c.updateHandler()
|
||||||
c.online = true
|
c.online = true
|
||||||
c.locks.authorizationReady.Unlock()
|
c.locks.authorizationReady.Unlock()
|
||||||
|
|
@ -259,7 +259,7 @@ func (c *Client) interactor() {
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Warn("Interactor is disconnected")
|
log.Warn("Interactor is disconnected")
|
||||||
c.locks.authorizerReadLock.Unlock()
|
c.locks.authorizerReadLock.Unlock()
|
||||||
return
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
stateType := state.AuthorizationStateType()
|
stateType := state.AuthorizationStateType()
|
||||||
|
|
@ -275,12 +275,12 @@ func (c *Client) interactor() {
|
||||||
if c.Session.Login != "" {
|
if c.Session.Login != "" {
|
||||||
c.authorizer.PhoneNumber <- c.Session.Login
|
c.authorizer.PhoneNumber <- c.Session.Login
|
||||||
} else {
|
} else {
|
||||||
c.wizardStageOrPrompt("login", "Please, enter your Telegram login via /login 12345, or use the Login Wizard via Ad-Hoc commands")
|
c.wizardStageOrPrompt(LoginStageLogin, "Please, enter your Telegram login via /login 12345, or use the Login Wizard via Ad-Hoc commands")
|
||||||
}
|
}
|
||||||
// stage 1: wait for auth code
|
// stage 1: wait for auth code
|
||||||
case client.TypeAuthorizationStateWaitCode:
|
case client.TypeAuthorizationStateWaitCode:
|
||||||
log.Warn("Waiting for authorization code...")
|
log.Warn("Waiting for authorization code...")
|
||||||
c.wizardStageOrPrompt("code", "Please, enter authorization code via /code 12345")
|
c.wizardStageOrPrompt(LoginStageCode, "Please, enter authorization code via /code 12345")
|
||||||
// stage 1b: wait for registration
|
// stage 1b: wait for registration
|
||||||
case client.TypeAuthorizationStateWaitRegistration:
|
case client.TypeAuthorizationStateWaitRegistration:
|
||||||
log.Warn("Waiting for full name...")
|
log.Warn("Waiting for full name...")
|
||||||
|
|
@ -288,10 +288,13 @@ func (c *Client) interactor() {
|
||||||
// stage 2: wait for 2fa
|
// stage 2: wait for 2fa
|
||||||
case client.TypeAuthorizationStateWaitPassword:
|
case client.TypeAuthorizationStateWaitPassword:
|
||||||
log.Warn("Waiting for 2FA password...")
|
log.Warn("Waiting for 2FA password...")
|
||||||
c.wizardStageOrPrompt("password", "Please, enter 2FA passphrase via /password 12345")
|
c.wizardStageOrPrompt(LoginStagePassword, "Please, enter 2FA passphrase via /password 12345")
|
||||||
}
|
}
|
||||||
c.locks.authorizerReadLock.Unlock()
|
c.locks.authorizerReadLock.Unlock()
|
||||||
}
|
}
|
||||||
|
if c.loginStage != LoginStageCancel {
|
||||||
|
c.wizardStageOrPrompt(LoginStagePreset, "Do you want to use a config preset? `/preset modern` enables brand new XMPP features, `/preset classic` targets legacy clients stuck in 00s. /pass proceeds to the next stage.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) forceClose() {
|
func (c *Client) forceClose() {
|
||||||
|
|
@ -322,6 +325,7 @@ func (c *Client) close() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) cancelAuth() {
|
func (c *Client) cancelAuth() {
|
||||||
|
c.wizardStageOrPrompt(LoginStageCancel, "")
|
||||||
c.StopLoginWizard()
|
c.StopLoginWizard()
|
||||||
c.close()
|
c.close()
|
||||||
c.Session.Login = ""
|
c.Session.Login = ""
|
||||||
|
|
|
||||||
|
|
@ -7,11 +7,32 @@ import (
|
||||||
"github.com/zelenin/go-tdlib/client"
|
"github.com/zelenin/go-tdlib/client"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type LoginStage string
|
||||||
|
const (
|
||||||
|
LoginStageNone LoginStage = ""
|
||||||
|
LoginStageLogin LoginStage = "login"
|
||||||
|
LoginStageCode LoginStage = "code"
|
||||||
|
LoginStagePassword LoginStage = "password"
|
||||||
|
LoginStagePreset LoginStage = "preset"
|
||||||
|
LoginStageMUC LoginStage = "muc"
|
||||||
|
LoginStageSuccess LoginStage = "success"
|
||||||
|
LoginStageCancel LoginStage = "cancel"
|
||||||
|
)
|
||||||
|
|
||||||
|
// setLoginStage updates loginState and triggers session initialization on login sucess
|
||||||
|
func (c *Client) setLoginStage(stage LoginStage) {
|
||||||
|
c.loginStage = stage
|
||||||
|
switch c.loginStage {
|
||||||
|
case LoginStageSuccess, LoginStageCancel:
|
||||||
|
c.locks.loginFinish.Done()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// StartLoginWizard initiates a loginWizard object
|
// StartLoginWizard initiates a loginWizard object
|
||||||
func (c *Client) StartLoginWizard(inCommand bool) {
|
func (c *Client) StartLoginWizard(inCommand bool) {
|
||||||
if c.loginWizard == nil {
|
if c.loginWizard == nil {
|
||||||
c.loginWizard = &loginWizardMetadata{
|
c.loginWizard = &loginWizardMetadata{
|
||||||
nextStage: make(chan string, 1),
|
nextStage: make(chan LoginStage, 1),
|
||||||
commandSent: inCommand,
|
commandSent: inCommand,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -32,7 +53,7 @@ func (c *Client) StopLoginWizard() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetLoginWizardNextStage waits for the next stage from the channel
|
// GetLoginWizardNextStage waits for the next stage from the channel
|
||||||
func (c *Client) GetLoginWizardNextStage() string {
|
func (c *Client) GetLoginWizardNextStage() LoginStage {
|
||||||
c.locks.loginWizardReadLock.Lock()
|
c.locks.loginWizardReadLock.Lock()
|
||||||
defer c.locks.loginWizardReadLock.Unlock()
|
defer c.locks.loginWizardReadLock.Unlock()
|
||||||
|
|
||||||
|
|
@ -45,25 +66,40 @@ func (c *Client) GetLoginWizardNextStage() string {
|
||||||
log.Debugf("yielded stage %v", nextStage)
|
log.Debugf("yielded stage %v", nextStage)
|
||||||
return nextStage
|
return nextStage
|
||||||
} else {
|
} else {
|
||||||
|
log.Debugf("commandSent is false")
|
||||||
|
|
||||||
if c.lastAuthorizationStateType == client.TypeAuthorizationStateWaitPhoneNumber ||
|
if c.lastAuthorizationStateType == client.TypeAuthorizationStateWaitPhoneNumber ||
|
||||||
c.lastAuthorizationStateType == client.TypeAuthorizationStateClosing ||
|
c.lastAuthorizationStateType == client.TypeAuthorizationStateClosing ||
|
||||||
c.Session.Login == "" {
|
c.Session.Login == "" {
|
||||||
return "login"
|
return LoginStageLogin
|
||||||
}
|
}
|
||||||
switch c.lastAuthorizationStateType {
|
switch c.lastAuthorizationStateType {
|
||||||
case client.TypeAuthorizationStateWaitCode:
|
case client.TypeAuthorizationStateWaitCode:
|
||||||
return "code"
|
return LoginStageCode
|
||||||
case client.TypeAuthorizationStateWaitPassword:
|
case client.TypeAuthorizationStateWaitPassword:
|
||||||
return "password"
|
return LoginStagePassword
|
||||||
|
}
|
||||||
|
|
||||||
|
switch c.loginStage {
|
||||||
|
case LoginStagePreset:
|
||||||
|
return LoginStageMUC
|
||||||
|
case LoginStageMUC:
|
||||||
|
return LoginStageNone
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return LoginStageNone
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) wizardStageOrPrompt(stage, message string) {
|
func (c *Client) wizardStageOrPrompt(stage LoginStage, message string) {
|
||||||
|
log.Debugf("loginStage: %v stage: %v", c.loginStage, stage)
|
||||||
|
if c.loginStage == stage {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
c.locks.loginWizardWriteLock.Lock()
|
c.locks.loginWizardWriteLock.Lock()
|
||||||
|
c.setLoginStage(stage)
|
||||||
if c.loginWizard == nil {
|
if c.loginWizard == nil {
|
||||||
c.locks.loginWizardWriteLock.Unlock()
|
c.locks.loginWizardWriteLock.Unlock()
|
||||||
if message != "" {
|
if message != "" {
|
||||||
|
|
|
||||||
|
|
@ -1454,24 +1454,15 @@ func handleSetQueryCommand(s xmpp.Sender, iq *stanza.IQ, command *stanza.Command
|
||||||
var warnString, errString string
|
var warnString, errString string
|
||||||
for _, field := range form.Fields {
|
for _, field := range form.Fields {
|
||||||
if len(field.ValuesList) > 0 {
|
if len(field.ValuesList) > 0 {
|
||||||
fieldValue := field.ValuesList[0]
|
|
||||||
|
|
||||||
if gateway.MessageOutgoingPermissionVersion == 0 && field.Var == "carbons" && fieldValue == "true" {
|
|
||||||
warnString = "The server did not allow to enable carbons"
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// 10. In accordance with Section 3.2.2.1 of XML Schema Part 2: Datatypes, the allowable
|
// 10. In accordance with Section 3.2.2.1 of XML Schema Part 2: Datatypes, the allowable
|
||||||
// lexical representations for the xs:boolean datatype are the strings "0" and "false"
|
// lexical representations for the xs:boolean datatype are the strings "0" and "false"
|
||||||
// for the concept 'false' and the strings "1" and "true" for the concept 'true';
|
// for the concept 'false' and the strings "1" and "true" for the concept 'true';
|
||||||
// implementations MUST support both styles of lexical representation.
|
// implementations MUST support both styles of lexical representation.
|
||||||
if persistence.PropertyType(field.Var) == persistence.PropertyTypeBool {
|
fieldValue := persistence.NormalizeProperty(field.Var, field.ValuesList[0])
|
||||||
if fieldValue == "0" {
|
|
||||||
fieldValue = "false"
|
if gateway.MessageOutgoingPermissionVersion == 0 && field.Var == "carbons" && fieldValue == "true" {
|
||||||
}
|
warnString = "The server did not allow to enable carbons"
|
||||||
if fieldValue == "1" {
|
continue
|
||||||
fieldValue = "true"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
oldValue, err := session.Session.Get(field.Var)
|
oldValue, err := session.Session.Get(field.Var)
|
||||||
|
|
@ -1524,7 +1515,7 @@ func handleSetQueryCommand(s xmpp.Sender, iq *stanza.IQ, command *stanza.Command
|
||||||
}
|
}
|
||||||
} else if !toOk && command.Node == "loginwizard" {
|
} else if !toOk && command.Node == "loginwizard" {
|
||||||
var session *telegram.Client
|
var session *telegram.Client
|
||||||
answer.Payload, cancelSend, session = loginWizardPayload(bare, form, resource)
|
answer.Payload, cancelSend, session = loginWizardPayload(bare, form, resource, command.Action)
|
||||||
|
|
||||||
log.Debugf("immediate loginwizard payload: %#v", answer.Payload)
|
log.Debugf("immediate loginwizard payload: %#v", answer.Payload)
|
||||||
if cancelSend {
|
if cancelSend {
|
||||||
|
|
@ -1685,7 +1676,7 @@ func handleSetQueryCommand(s xmpp.Sender, iq *stanza.IQ, command *stanza.Command
|
||||||
log.Debugf("form: %#v", form)
|
log.Debugf("form: %#v", form)
|
||||||
} else if !toOk && command.Node == "loginwizard" {
|
} else if !toOk && command.Node == "loginwizard" {
|
||||||
var session *telegram.Client
|
var session *telegram.Client
|
||||||
answer.Payload, cancelSend, session = loginWizardPayload(bare, nil, resource)
|
answer.Payload, cancelSend, session = loginWizardPayload(bare, nil, resource, "")
|
||||||
|
|
||||||
log.Debugf("immediate loginwizard payload: %#v", answer.Payload)
|
log.Debugf("immediate loginwizard payload: %#v", answer.Payload)
|
||||||
if cancelSend {
|
if cancelSend {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package xmpp
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"dev.narayana.im/narayana/telegabber/persistence"
|
||||||
"dev.narayana.im/narayana/telegabber/telegram"
|
"dev.narayana.im/narayana/telegabber/telegram"
|
||||||
"dev.narayana.im/narayana/telegabber/xmpp/gateway"
|
"dev.narayana.im/narayana/telegabber/xmpp/gateway"
|
||||||
|
|
||||||
|
|
@ -11,7 +12,7 @@ import (
|
||||||
"gosrc.io/xmpp/stanza"
|
"gosrc.io/xmpp/stanza"
|
||||||
)
|
)
|
||||||
|
|
||||||
func loginWizardPayload(bare string, requestForm *stanza.Form, resource string) (payload *stanza.Command, cancelSend bool, returnSession *telegram.Client) {
|
func loginWizardPayload(bare string, requestForm *stanza.Form, resource string, action string) (payload *stanza.Command, cancelSend bool, returnSession *telegram.Client) {
|
||||||
payload = &stanza.Command{
|
payload = &stanza.Command{
|
||||||
SessionId: "loginwizard",
|
SessionId: "loginwizard",
|
||||||
Node: "loginwizard",
|
Node: "loginwizard",
|
||||||
|
|
@ -21,9 +22,13 @@ func loginWizardPayload(bare string, requestForm *stanza.Form, resource string)
|
||||||
if ok {
|
if ok {
|
||||||
returnSession = session
|
returnSession = session
|
||||||
|
|
||||||
|
var command string
|
||||||
|
|
||||||
if requestForm == nil {
|
if requestForm == nil {
|
||||||
session.StartLoginWizard(false)
|
session.StartLoginWizard(false)
|
||||||
cancelSend = true
|
cancelSend = true
|
||||||
|
} else if action == stanza.CommandActionComplete || action == stanza.CommandActionExecute {
|
||||||
|
command = "/finish"
|
||||||
} else {
|
} else {
|
||||||
if len(requestForm.Fields) != 1 {
|
if len(requestForm.Fields) != 1 {
|
||||||
setCommandPayloadError(payload, "Hey, don't tinker with the form!")
|
setCommandPayloadError(payload, "Hey, don't tinker with the form!")
|
||||||
|
|
@ -35,24 +40,35 @@ func loginWizardPayload(bare string, requestForm *stanza.Form, resource string)
|
||||||
setCommandPayloadError(payload, "No value")
|
setCommandPayloadError(payload, "No value")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
value := field.ValuesList[0]
|
||||||
switch field.Var {
|
switch field.Var {
|
||||||
case "login", "code", "password":
|
case "login", "code", "password", "preset":
|
||||||
|
if field.Var == "preset" && value == "" {
|
||||||
|
command = "/pass"
|
||||||
|
} else {
|
||||||
|
command = fmt.Sprintf("/%v %v", field.Var, value)
|
||||||
|
}
|
||||||
|
case "muc":
|
||||||
|
fieldValue := persistence.NormalizeProperty(field.Var, value)
|
||||||
|
command = fmt.Sprintf("/config muc %v", fieldValue)
|
||||||
default:
|
default:
|
||||||
setCommandPayloadError(payload, "Unknown field")
|
setCommandPayloadError(payload, "Unknown field")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
session.StartLoginWizard(true)
|
|
||||||
response, success := session.ProcessTransportCommand(fmt.Sprintf("/%v %v", field.Var, field.ValuesList[0]), resource)
|
|
||||||
if !success {
|
|
||||||
setCommandPayloadError(payload, response)
|
|
||||||
session.StopLoginWizard()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
cancelSend = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if command != "" {
|
||||||
|
session.StartLoginWizard(true)
|
||||||
|
response, success := session.ProcessTransportCommand(command, resource)
|
||||||
|
if !success {
|
||||||
|
setCommandPayloadError(payload, response)
|
||||||
|
session.StopLoginWizard()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelSend = true
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
setCommandPayloadError(payload, fmt.Sprintf("Session is not initialized, add the transport (%v) to contacts first", gateway.Jid.Bare()))
|
setCommandPayloadError(payload, fmt.Sprintf("Session is not initialized, add the transport (%v) to contacts first", gateway.Jid.Bare()))
|
||||||
}
|
}
|
||||||
|
|
@ -66,33 +82,89 @@ func sendLoginWizardResponse(component *xmpp.Component, answer *stanza.IQ, sessi
|
||||||
Node: "loginwizard",
|
Node: "loginwizard",
|
||||||
}
|
}
|
||||||
|
|
||||||
nextStage := "login"
|
nextStage := telegram.LoginStageLogin
|
||||||
if session != nil {
|
if session != nil {
|
||||||
nextStage = session.GetLoginWizardNextStage()
|
nextStage = session.GetLoginWizardNextStage()
|
||||||
}
|
}
|
||||||
log.Debugf("nextStage: %v", nextStage)
|
log.Debugf("nextStage: %v", nextStage)
|
||||||
|
|
||||||
if nextStage == "cancel" {
|
if nextStage == telegram.LoginStageNone || nextStage == telegram.LoginStageCancel {
|
||||||
setCommandPayloadError(payload, "Cancelled")
|
setCommandPayloadError(payload, "Cancelled")
|
||||||
session.StopLoginWizard()
|
session.StopLoginWizard()
|
||||||
} else if nextStage == "success" {
|
} else if nextStage == telegram.LoginStageSuccess {
|
||||||
payload.Status = stanza.CommandStatusCompleted
|
payload.Status = stanza.CommandStatusCompleted
|
||||||
session.StopLoginWizard()
|
session.StopLoginWizard()
|
||||||
} else {
|
} else {
|
||||||
required := ""
|
required := ""
|
||||||
|
var fieldType string
|
||||||
|
var finishAction bool
|
||||||
|
var options []stanza.Option
|
||||||
|
var note string
|
||||||
|
|
||||||
|
switch nextStage {
|
||||||
|
case telegram.LoginStagePreset:
|
||||||
|
fieldType = stanza.FieldTypeListSingle
|
||||||
|
finishAction = true
|
||||||
|
options = []stanza.Option{
|
||||||
|
stanza.Option{
|
||||||
|
ValuesList: []string{""},
|
||||||
|
},
|
||||||
|
stanza.Option{
|
||||||
|
Label: "Modern",
|
||||||
|
ValuesList: []string{"modern"},
|
||||||
|
},
|
||||||
|
stanza.Option{
|
||||||
|
Label: "Classic",
|
||||||
|
ValuesList: []string{"classic"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
note = "Do you want to use a config preset?\nModern enables brand new XMPP features,\nClassic targets legacy clients stuck in 00s."
|
||||||
|
case telegram.LoginStageMUC:
|
||||||
|
fieldType = stanza.FieldTypeBool
|
||||||
|
finishAction = true
|
||||||
|
|
||||||
|
value, err := session.Session.Get("muc")
|
||||||
|
if err != nil {
|
||||||
|
log.Error("Achtung! Programming error in retrieving MUC config option")
|
||||||
|
value = "false"
|
||||||
|
}
|
||||||
|
|
||||||
|
options = append(options, stanza.Option{
|
||||||
|
ValuesList: []string{value},
|
||||||
|
})
|
||||||
|
note = "Enable MUCs? Telegabber still supports the legacy group-to-PM mapping too."
|
||||||
|
}
|
||||||
|
|
||||||
form := stanza.Form{
|
form := stanza.Form{
|
||||||
Type: stanza.FormTypeForm,
|
Type: stanza.FormTypeForm,
|
||||||
Title: "Login Wizard",
|
Title: "Login Wizard",
|
||||||
Fields: []*stanza.Field{
|
Fields: []*stanza.Field{
|
||||||
&stanza.Field{
|
&stanza.Field{
|
||||||
Var: nextStage,
|
Var: string(nextStage),
|
||||||
Label: nextStage,
|
Label: string(nextStage),
|
||||||
Required: &required,
|
Required: &required,
|
||||||
|
Type: fieldType,
|
||||||
|
Options: options,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
payload.Status = stanza.CommandStatusExecuting
|
payload.Status = stanza.CommandStatusExecuting
|
||||||
payload.CommandElements = append(payload.CommandElements, &form)
|
payload.CommandElements = append(payload.CommandElements, &form)
|
||||||
|
|
||||||
|
actions := stanza.Actions{
|
||||||
|
Next: &struct{}{},
|
||||||
|
}
|
||||||
|
if finishAction {
|
||||||
|
actions.Complete = &struct{}{}
|
||||||
|
}
|
||||||
|
payload.CommandElements = append(payload.CommandElements, &actions)
|
||||||
|
|
||||||
|
if note != "" {
|
||||||
|
payload.CommandElements = append(payload.CommandElements, &stanza.Note{
|
||||||
|
Text: note,
|
||||||
|
Type: stanza.CommandNoteTypeInfo,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
answer.Payload = payload
|
answer.Payload = payload
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue