package xmpp import ( "fmt" "dev.narayana.im/narayana/telegabber/telegram" "dev.narayana.im/narayana/telegabber/xmpp/gateway" "gosrc.io/xmpp/stanza" ) // buildOmemoTransportFormFields builds the initial (no form submitted yet) // field list for the transport-level "/omemo" ad-hoc command: a single // list-single field for the account-wide mode, pre-selected to the current // value. Plugs into handleSetQueryCommand's shared per-command // field-then-form-then-answer construction, the same way "config"'s own // field-building does. func buildOmemoTransportFormFields(session *telegram.Client) []*stanza.Field { mode, _ := session.Session.Get("omemo") return []*stanza.Field{ { Var: "mode", Label: "OMEMO mode", Type: stanza.FieldTypeListSingle, Options: []stanza.Option{ {Label: "Automatic (default)", ValuesList: []string{"auto"}}, {Label: "Always on", ValuesList: []string{"on"}}, {Label: "Always off", ValuesList: []string{"off"}}, }, ValuesList: []string{mode}, }, } } // buildOmemoChatFormFields builds the initial field list for the // chat-scoped "/omemo" ad-hoc command: a read-only effective-status field, // a list-single override control, and two list-single "block"/"accept" // fields - each an empty-by-default choice (no selection = no action) // among the CURRENT opposite-state devices (block's options are the // currently-trusted devices, accept's are the currently-blocked ones). // This mirrors handleSetQueryCommand's existing mute/kick/ban/unmute/unban // field-building exactly (a list-single of opposite-state members with a // blank leading option), rather than a separate reported/item table plus // list-multi fields - one form field per action, same as those commands. func buildOmemoChatFormFields(session *telegram.Client, chatID int64) []*stanza.Field { status, err := session.GetOmemoChatStatus(chatID) if err != nil { return []*stanza.Field{ {Type: stanza.FieldTypeFixed, Label: "Error", ValuesList: []string{err.Error()}}, } } overrideValue := "" if status.OverrideSet { overrideValue = omemoOnOff(status.Override) } fields := []*stanza.Field{ { Var: "effective", Label: "Effective right now", Type: stanza.FieldTypeFixed, ValuesList: []string{fmt.Sprintf("%s (account-wide mode: %s)", omemoOnOff(status.Effective), status.Mode)}, }, { Var: "override", Label: "Override for this chat", Type: stanza.FieldTypeListSingle, Options: []stanza.Option{ {Label: "Follow account-wide mode", ValuesList: []string{""}}, {Label: "Always on", ValuesList: []string{"on"}}, {Label: "Always off", ValuesList: []string{"off"}}, }, ValuesList: []string{overrideValue}, }, } // allow an empty selection ("no action"), same as mute/unmute's own // blank leading option blockOptions := []stanza.Option{{ValuesList: []string{""}}} acceptOptions := []stanza.Option{{ValuesList: []string{""}}} for _, d := range status.Devices { option := stanza.Option{Label: d.ID, ValuesList: []string{d.ID}} if d.Trusted { blockOptions = append(blockOptions, option) } else { acceptOptions = append(acceptOptions, option) } } return append(fields, &stanza.Field{Var: "block", Label: "Block device", Type: stanza.FieldTypeListSingle, Options: blockOptions}, &stanza.Field{Var: "accept", Label: "Accept (unblock) device", Type: stanza.FieldTypeListSingle, Options: acceptOptions}, ) } // submitOmemoTransportForm applies a submitted transport-level "/omemo" // form. func submitOmemoTransportForm(session *telegram.Client, form *stanza.Form) *stanza.Command { var mode string for _, field := range form.Fields { if field != nil && field.Var == "mode" && len(field.ValuesList) > 0 { mode = field.ValuesList[0] } } var note stanza.Note if value, err := session.Session.Set("omemo", mode); err != nil { note = stanza.Note{Text: err.Error(), Type: stanza.CommandNoteTypeErr} } else { gateway.DirtySessions = true note = stanza.Note{Text: fmt.Sprintf("omemo mode set to %s", value), Type: stanza.CommandNoteTypeInfo} } return &stanza.Command{ SessionId: "omemo", Node: "omemo", Status: stanza.CommandStatusCompleted, CommandElements: []stanza.CommandElement{¬e}, } } // submitOmemoChatForm applies a submitted chat-scoped "/omemo" form: the // override change (if submitted), then at most one block and one accept // (each field is a single empty-or-one-device selection, not a multi-pick // - see buildOmemoChatFormFields) - all in one round trip, since XEP-0050 // single-stage commands only get one. func submitOmemoChatForm(session *telegram.Client, chatID int64, form *stanza.Form) *stanza.Command { var overrideValue string var overrideSubmitted bool var blockID, acceptID string for _, field := range form.Fields { if field == nil { continue } switch field.Var { case "override": overrideSubmitted = true if len(field.ValuesList) > 0 { overrideValue = field.ValuesList[0] } case "block": if len(field.ValuesList) > 0 { blockID = field.ValuesList[0] } case "accept": if len(field.ValuesList) > 0 { acceptID = field.ValuesList[0] } } } var notes []stanza.CommandElement if overrideSubmitted { switch overrideValue { case "": if err := session.SetOmemoChatOverride(chatID, nil); err != nil { notes = append(notes, &stanza.Note{Text: err.Error(), Type: stanza.CommandNoteTypeErr}) } else { notes = append(notes, &stanza.Note{Text: "OMEMO override cleared for this chat", Type: stanza.CommandNoteTypeInfo}) } case "on", "off": on := overrideValue == "on" if err := session.SetOmemoChatOverride(chatID, &on); err != nil { notes = append(notes, &stanza.Note{Text: err.Error(), Type: stanza.CommandNoteTypeErr}) } else { notes = append(notes, &stanza.Note{Text: fmt.Sprintf("OMEMO forced %s for this chat", overrideValue), Type: stanza.CommandNoteTypeInfo}) } } } if blockID != "" { if err := session.SetOmemoDeviceBlocked(chatID, blockID, true); err != nil { notes = append(notes, &stanza.Note{Text: fmt.Sprintf("Failed to block device %s: %v", blockID, err), Type: stanza.CommandNoteTypeErr}) } else { notes = append(notes, &stanza.Note{Text: fmt.Sprintf("Device %s blocked", blockID), Type: stanza.CommandNoteTypeInfo}) } } if acceptID != "" { if err := session.SetOmemoDeviceBlocked(chatID, acceptID, false); err != nil { notes = append(notes, &stanza.Note{Text: fmt.Sprintf("Failed to accept device %s: %v", acceptID, err), Type: stanza.CommandNoteTypeErr}) } else { notes = append(notes, &stanza.Note{Text: fmt.Sprintf("Device %s accepted", acceptID), Type: stanza.CommandNoteTypeInfo}) } } if len(notes) == 0 { notes = append(notes, &stanza.Note{Text: "No changes", Type: stanza.CommandNoteTypeInfo}) } return &stanza.Command{ SessionId: "omemo", Node: "omemo", Status: stanza.CommandStatusCompleted, CommandElements: notes, } } func omemoOnOff(b bool) string { if b { return "on" } return "off" }