Ad-Hoc config editor

This commit is contained in:
Bohdan Horbeshko 2025-03-22 18:11:26 -04:00
parent 7ebcdb0826
commit 0368b8cad8
5 changed files with 206 additions and 74 deletions

View file

@ -2,7 +2,7 @@
COMMIT := $(shell git rev-parse --short HEAD) COMMIT := $(shell git rev-parse --short HEAD)
TD_COMMIT := "5bbfc1cf5dab94f82e02f3430ded7241d4653551" TD_COMMIT := "5bbfc1cf5dab94f82e02f3430ded7241d4653551"
VERSION := "v1.10.1" VERSION := "v1.10.2"
MAKEOPTS := "-j4" MAKEOPTS := "-j4"
all: all:

View file

@ -51,7 +51,13 @@ type Session struct {
IgnoreGroupDeletions bool `yaml:":ignoregroupdeletions"` IgnoreGroupDeletions bool `yaml:":ignoregroupdeletions"`
} }
var configKeys = []string{ const (
PropertyTypeUnknown byte = iota
PropertyTypeString
PropertyTypeBool
)
var ConfigKeys = []string{
"timezone", "timezone",
"keeponline", "keeponline",
"rawmessages", "rawmessages",
@ -181,7 +187,7 @@ func (s *Session) ToMap() map[string]string {
defer sessionsLock.Unlock() defer sessionsLock.Unlock()
m := make(map[string]string) m := make(map[string]string)
for _, configKey := range configKeys { for _, configKey := range ConfigKeys {
value, _ := s.get(configKey) value, _ := s.get(configKey)
m[configKey] = value m[configKey] = value
} }
@ -266,6 +272,18 @@ func (s *Session) Set(key string, value string) (string, error) {
return "", errors.New("Unknown session property") return "", errors.New("Unknown session property")
} }
// PropertyType determines the property type
func PropertyType(key string) byte {
switch key {
case "timezone":
return PropertyTypeString
case "keeponline", "rawmessages", "asciiarrows", "oobmode", "carbons", "hideids",
"receipts", "nativeedits", "ignoregroupdeletions":
return PropertyTypeBool
}
return PropertyTypeUnknown
}
// TimezoneToLocation tries to convert config timezone to location // TimezoneToLocation tries to convert config timezone to location
func (s *Session) TimezoneToLocation() *time.Location { func (s *Session) TimezoneToLocation() *time.Location {
time, err := time.Parse("-07:00", s.Timezone) time, err := time.Parse("-07:00", s.Timezone)

View file

@ -16,7 +16,7 @@ import (
goxmpp "gosrc.io/xmpp" goxmpp "gosrc.io/xmpp"
) )
var version string = "1.10.1" var version string = "1.10.2"
var commit string var commit string
var sm *goxmpp.StreamManager var sm *goxmpp.StreamManager

View file

@ -9,6 +9,7 @@ import (
"time" "time"
"unicode" "unicode"
"dev.narayana.im/narayana/telegabber/persistence"
"dev.narayana.im/narayana/telegabber/xmpp/gateway" "dev.narayana.im/narayana/telegabber/xmpp/gateway"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
@ -109,8 +110,15 @@ var chatCommands = map[string]command{
var transportConfigurationOptions = map[string]configurationOption{ var transportConfigurationOptions = map[string]configurationOption{
"timezone": configurationOption{"<timezone>", "adjust timezone for Telegram user statuses (example: +02:00)"}, "timezone": configurationOption{"<timezone>", "adjust timezone for Telegram user statuses (example: +02:00)"},
"keeponline": configurationOption{"<bool>", "always keep telegram session online and rely on jabber offline messages (example: true)"}, "keeponline": configurationOption{"<bool>", "always keep telegram session online and rely on jabber offline messages (true/false)"},
"rawmessages": configurationOption{"<bool>", "do not add additional info (message id, origin etc.) to incoming messages (example: true)"}, "rawmessages": configurationOption{"<bool>", "do not add additional info (message id, origin etc.) to incoming messages (true/false)"},
"asciiarrows": configurationOption{"<bool>", "replace some Unicode symbols with ASCII alternatives for better compatibility (true/false)"},
"oobmode": configurationOption{"<bool>", "use XEP-0066 (OOB); pros: some modern clients won't show images without it, cons: very restricted, Tkabber would flood with popups (true/false)"},
"carbons": configurationOption{"<bool>", "send carbons to your another clients, will turn on only if supported by the server (true/false)"},
"hideids": configurationOption{"<bool>", "hide message IDs from message info (true/false)"},
"receipts": configurationOption{"<bool>", "if enabled, XMPP read receipts are synced to Telegram, otherwise, messages are marked as read automatically (true/false)"},
"nativeedits": configurationOption{"<bool>", "if possible, edit XMPP messages instead of showing Telegram edits as separate messages (true/false)"},
"ignoregroupdeletions": configurationOption{"<bool>", "suppress message deletion messages in group chats (true/false)"},
} }
type command struct { type command struct {
@ -222,7 +230,8 @@ func (c *Client) helpString(typ CommandType, chatId int64) string {
if typ == CommandTypeTransport { if typ == CommandTypeTransport {
str.WriteString("Configuration options\n") str.WriteString("Configuration options\n")
for name, option := range transportConfigurationOptions { for _, name := range persistence.ConfigKeys {
option := transportConfigurationOptions[name]
str.WriteString(name) str.WriteString(name)
str.WriteString(" ") str.WriteString(" ")
str.WriteString(option.arguments) str.WriteString(option.arguments)
@ -461,7 +470,6 @@ func (c *Client) ProcessTransportCommand(cmdline string, resource string) (strin
} }
case "config": case "config":
if len(args) > 1 { if len(args) > 1 {
var msg string
if gateway.MessageOutgoingPermissionVersion == 0 && args[0] == "carbons" && args[1] == "true" { if gateway.MessageOutgoingPermissionVersion == 0 && args[0] == "carbons" && args[1] == "true" {
return "The server did not allow to enable carbons", false return "The server did not allow to enable carbons", false
} }
@ -472,7 +480,7 @@ func (c *Client) ProcessTransportCommand(cmdline string, resource string) (strin
} }
gateway.DirtySessions = true gateway.DirtySessions = true
return fmt.Sprintf("%s%s set to %s", msg, args[0], value), true return fmt.Sprintf("%s set to %s", args[0], value), true
} else if len(args) > 0 { } else if len(args) > 0 {
value, err := c.Session.Get(args[0]) value, err := c.Session.Get(args[0])
if err != nil { if err != nil {
@ -483,7 +491,12 @@ func (c *Client) ProcessTransportCommand(cmdline string, resource string) (strin
} }
var entries []string var entries []string
for key, value := range c.Session.ToMap() { for _, key := range persistence.ConfigKeys {
value, err := c.Session.Get(key)
if err != nil {
log.Errorf("Achtung! Programming error in sessions with key %v", key)
continue
}
entries = append(entries, fmt.Sprintf("%s is set to %s", key, value)) entries = append(entries, fmt.Sprintf("%s is set to %s", key, value))
} }

View file

@ -877,6 +877,74 @@ func handleSetQueryCommand(s xmpp.Sender, iq *stanza.IQ, command *stanza.Command
cmdType = telegram.CommandTypeTransport cmdType = telegram.CommandTypeTransport
} }
if form != nil { if form != nil {
if command.Node == "config" {
session, ok := sessions[bare]
if ok {
var infoStrings []string
var warnString, errString string
for _, field := range form.Fields {
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
// 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';
// implementations MUST support both styles of lexical representation.
if persistence.PropertyType(field.Var) == persistence.PropertyTypeBool {
if fieldValue == "0" {
fieldValue = "false"
}
if fieldValue == "1" {
fieldValue = "true"
}
}
oldValue, err := session.Session.Get(field.Var)
if err != nil || oldValue != fieldValue {
value, err := session.Session.Set(field.Var, fieldValue)
if err != nil {
errString = fmt.Sprintf("Error for field %v: %v, aborting", field.Var, err.Error())
break
}
infoStrings = append(infoStrings, fmt.Sprintf("%s set to %s", field.Var, value))
gateway.DirtySessions = true
}
}
}
var elements []stanza.CommandElement
if errString != "" {
elements = append(elements, &stanza.Note{
Text: errString,
Type: stanza.CommandNoteTypeErr,
})
}
if warnString != "" {
elements = append(elements, &stanza.Note{
Text: warnString,
Type: stanza.CommandNoteTypeWarn,
})
}
for _, infoString := range infoStrings {
elements = append(elements, &stanza.Note{
Text: infoString,
Type: stanza.CommandNoteTypeInfo,
})
}
answer.Payload = &stanza.Command{
SessionId: command.Node,
Node: command.Node,
Status: stanza.CommandStatusCompleted,
CommandElements: elements,
}
}
} else {
// just for the case the client messed the order somehow // just for the case the client messed the order somehow
sort.Slice(form.Fields, func(i int, j int) bool { sort.Slice(form.Fields, func(i int, j int) bool {
iField := form.Fields[i] iField := form.Fields[i]
@ -900,11 +968,43 @@ func handleSetQueryCommand(s xmpp.Sender, iq *stanza.IQ, command *stanza.Command
} }
cmdString = cmd.String() cmdString = cmd.String()
}
} else { } else {
if command.Action == "" || command.Action == stanza.CommandActionExecute { if command.Action == "" || command.Action == stanza.CommandActionExecute {
cmd, ok := telegram.GetCommand(cmdType, command.Node) cmd, ok := telegram.GetCommand(cmdType, command.Node)
if ok && len(cmd.Arguments) > 0 { if ok && len(cmd.Arguments) > 0 {
var fields []*stanza.Field var fields []*stanza.Field
if command.Node == "config" {
session, ok := sessions[bare]
if ok {
for _, key := range persistence.ConfigKeys {
// no reason to display the item if carbons won't work
if key == "carbons" && gateway.MessageOutgoingPermissionVersion == 0 {
continue
}
value, err := session.Session.Get(key)
if err != nil {
log.Errorf("Achtung! Programming error in sessions with key %v", key)
continue
}
var fieldType string
if persistence.PropertyType(key) == persistence.PropertyTypeBool {
fieldType = stanza.FieldTypeBool
}
field := stanza.Field{
Var: key,
Label: key,
Type: fieldType,
ValuesList: []string{value},
}
fields = append(fields, &field)
log.Debugf("field: %#v", field)
}
}
} else {
for i, arg := range cmd.Arguments { for i, arg := range cmd.Arguments {
var required *string var required *string
if i < cmd.RequiredArgs { if i < cmd.RequiredArgs {
@ -958,6 +1058,7 @@ func handleSetQueryCommand(s xmpp.Sender, iq *stanza.IQ, command *stanza.Command
fields = append(fields, &field) fields = append(fields, &field)
log.Debugf("field: %#v", field) log.Debugf("field: %#v", field)
} }
}
form := stanza.Form{ form := stanza.Form{
Type: stanza.FormTypeForm, Type: stanza.FormTypeForm,
Title: command.Node, Title: command.Node,