mirror of
https://dev.narayana.im/narayana/telegabber.git
synced 2026-08-05 12:17:06 +00:00
Process MUC kicking
This commit is contained in:
parent
dd8267df2c
commit
1d165731a2
4 changed files with 175 additions and 26 deletions
|
|
@ -855,11 +855,7 @@ func (c *Client) ProcessChatCommand(chatID int64, cmdline string) (string, bool,
|
||||||
return "Contact not found", true, false
|
return "Contact not found", true, false
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = c.client.SetChatMemberStatus(&client.SetChatMemberStatusRequest{
|
err = c.Kick(chatID, contact.Id, "")
|
||||||
ChatId: chatID,
|
|
||||||
MemberId: &client.MessageSenderUser{UserId: contact.Id},
|
|
||||||
Status: &client.ChatMemberStatusLeft{},
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err.Error(), true, false
|
return err.Error(), true, false
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -808,6 +808,25 @@ func (c *Client) GetMyMUCNickname(chatID int64) (string, bool) {
|
||||||
return member.Nickname, true
|
return member.Nickname, true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetMUCMemberIdByNickname looks up the telegram ID by the MUC nickname (slow yet!)
|
||||||
|
func (c *Client) GetMUCMemberIdByNickname(chatID int64, nickname string) int64 {
|
||||||
|
c.locks.mucCacheLock.Lock()
|
||||||
|
defer c.locks.mucCacheLock.Unlock()
|
||||||
|
|
||||||
|
mucState, ok := c.mucCache[chatID]
|
||||||
|
if !ok || mucState == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
for memberId, member := range mucState.Members {
|
||||||
|
if member.Nickname == nickname {
|
||||||
|
return memberId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
// NewPinnedMessage sends a text message and pins it right away
|
// NewPinnedMessage sends a text message and pins it right away
|
||||||
func (c *Client) NewPinnedMessage(chatID int64, text, returnJid string) bool {
|
func (c *Client) NewPinnedMessage(chatID int64, text, returnJid string) bool {
|
||||||
c.locks.pinOutboxLock.Lock()
|
c.locks.pinOutboxLock.Lock()
|
||||||
|
|
@ -1591,6 +1610,11 @@ func (c *Client) ProcessIncomingMessage(chatId int64, message *client.Message) {
|
||||||
groupChatFrom := ""
|
groupChatFrom := ""
|
||||||
groupChatTos := []string{}
|
groupChatTos := []string{}
|
||||||
if c.Session.MUC && c.IsGroup(chat) {
|
if c.Session.MUC && c.IsGroup(chat) {
|
||||||
|
if message.Content.MessageContentType() == client.TypeMessageChatDeleteMember {
|
||||||
|
deleteMember, _ := message.Content.(*client.MessageChatDeleteMember)
|
||||||
|
c.kickMemberFromMUC(chatId, deleteMember.UserId, c.GetMUCNickname(deleteMember.UserId))
|
||||||
|
}
|
||||||
|
|
||||||
senderId := c.getMessageSenderId(message)
|
senderId := c.getMessageSenderId(message)
|
||||||
if senderId == 0 {
|
if senderId == 0 {
|
||||||
log.Errorf("Invalid sender id for message %#v", message)
|
log.Errorf("Invalid sender id for message %#v", message)
|
||||||
|
|
@ -2623,3 +2647,39 @@ func (c *Client) MigrateToMUCs() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Kick kicks
|
||||||
|
func (c *Client) Kick(chatID, userID int64, nickname string) error {
|
||||||
|
_, err := c.client.SetChatMemberStatus(&client.SetChatMemberStatusRequest{
|
||||||
|
ChatId: chatID,
|
||||||
|
MemberId: &client.MessageSenderUser{UserId: userID},
|
||||||
|
Status: &client.ChatMemberStatusLeft{},
|
||||||
|
})
|
||||||
|
if err != nil && nickname != "" {
|
||||||
|
c.kickMemberFromMUC(chatID, userID, nickname)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Client) kickMemberFromMUC(chatID, userID int64, nickname string) {
|
||||||
|
unavailableStatusCodes := []uint16{307}
|
||||||
|
if c.me != nil && userID == c.me.Id {
|
||||||
|
unavailableStatusCodes = append(unavailableStatusCodes, 110)
|
||||||
|
}
|
||||||
|
c.sendPresence(
|
||||||
|
gateway.SPType("unavailable"),
|
||||||
|
gateway.SPFrom(gateway.MUCNODE(chatID)),
|
||||||
|
gateway.SPResource(nickname),
|
||||||
|
gateway.SPImmed(true),
|
||||||
|
gateway.SPMUCAffiliation("none"),
|
||||||
|
gateway.SPMUCStatusCodes(unavailableStatusCodes),
|
||||||
|
gateway.SPMUCJid(gateway.CHATJID(userID, true)),
|
||||||
|
)
|
||||||
|
|
||||||
|
c.locks.mucCacheLock.Lock()
|
||||||
|
mucState, ok := c.mucCache[chatID]
|
||||||
|
if ok && mucState != nil {
|
||||||
|
delete(mucState.Members, userID)
|
||||||
|
}
|
||||||
|
c.locks.mucCacheLock.Unlock()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -307,6 +307,21 @@ type EmptySubject struct {
|
||||||
XMLName xml.Name `xml:"subject"`
|
XMLName xml.Name `xml:"subject"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// QueryMucAdmin is from XEP-0045
|
||||||
|
type QueryMucAdmin struct {
|
||||||
|
XMLName xml.Name `xml:"http://jabber.org/protocol/muc#admin query"`
|
||||||
|
Item QueryMucAdminItem `xml:"item"`
|
||||||
|
ResultSet *stanza.ResultSet `xml:"set,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryMucAdminItem is a child element from XEP-0045
|
||||||
|
type QueryMucAdminItem struct {
|
||||||
|
XMLName xml.Name `xml:"item"`
|
||||||
|
Nick string `xml:"nick,attr"`
|
||||||
|
Role string `xml:"role,attr"`
|
||||||
|
Reason string `xml:"reason,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// Namespace is a namespace!
|
// Namespace is a namespace!
|
||||||
func (c PresenceNickExtension) Namespace() string {
|
func (c PresenceNickExtension) Namespace() string {
|
||||||
return c.XMLName.Space
|
return c.XMLName.Space
|
||||||
|
|
@ -392,6 +407,16 @@ func (ClientMessage) Name() string {
|
||||||
return "message"
|
return "message"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Namespace is a namespace!
|
||||||
|
func (c QueryMucAdmin) Namespace() string {
|
||||||
|
return c.XMLName.Space
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSet getsets!
|
||||||
|
func (c QueryMucAdmin) GetSet() *stanza.ResultSet {
|
||||||
|
return c.ResultSet
|
||||||
|
}
|
||||||
|
|
||||||
// NewReplyFallback initializes a fallback range
|
// NewReplyFallback initializes a fallback range
|
||||||
func NewReplyFallback(start uint64, end uint64) Fallback {
|
func NewReplyFallback(start uint64, end uint64) Fallback {
|
||||||
return Fallback{
|
return Fallback{
|
||||||
|
|
@ -513,4 +538,10 @@ func init() {
|
||||||
"urn:xmpp:sid:0",
|
"urn:xmpp:sid:0",
|
||||||
"origin-id",
|
"origin-id",
|
||||||
}, MessageOriginId{})
|
}, MessageOriginId{})
|
||||||
|
|
||||||
|
// muc admin query
|
||||||
|
stanza.TypeRegistry.MapExtension(stanza.PKTIQ, xml.Name{
|
||||||
|
"http://jabber.org/protocol/muc#admin",
|
||||||
|
"query",
|
||||||
|
}, QueryMucAdmin{})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
104
xmpp/handlers.go
104
xmpp/handlers.go
|
|
@ -72,9 +72,9 @@ func HandleIq(s xmpp.Sender, p stanza.Packet) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else if iq.Type == stanza.IQTypeSet {
|
} else if iq.Type == stanza.IQTypeSet {
|
||||||
query, ok := iq.Payload.(*extensions.QueryRegister)
|
queryRegister, ok := iq.Payload.(*extensions.QueryRegister)
|
||||||
if ok {
|
if ok {
|
||||||
go handleSetQueryRegister(s, iq, query)
|
go handleSetQueryRegister(s, iq, queryRegister)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
command, ok := iq.Payload.(*stanza.Command)
|
command, ok := iq.Payload.(*stanza.Command)
|
||||||
|
|
@ -82,6 +82,11 @@ func HandleIq(s xmpp.Sender, p stanza.Packet) {
|
||||||
go handleSetQueryCommand(s, iq, command)
|
go handleSetQueryCommand(s, iq, command)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
queryMucAdmin, ok := iq.Payload.(*extensions.QueryMucAdmin)
|
||||||
|
if ok {
|
||||||
|
go handleSetQueryMucAdmin(s, iq, queryMucAdmin)
|
||||||
|
return
|
||||||
|
}
|
||||||
} else if iq.Type == stanza.IQTypeResult {
|
} else if iq.Type == stanza.IQTypeResult {
|
||||||
discoInfo, ok := iq.Payload.(*stanza.DiscoInfo)
|
discoInfo, ok := iq.Payload.(*stanza.DiscoInfo)
|
||||||
if ok {
|
if ok {
|
||||||
|
|
@ -1111,7 +1116,7 @@ func handleGetQueryRegister(s xmpp.Sender, iq *stanza.IQ) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
query := extensions.QueryRegister{}
|
query := extensions.QueryRegister{}
|
||||||
iqAnswerSetError(answer, &query, 404)
|
iqAnswerRegisterSetError(answer, &query, 404)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1151,12 +1156,12 @@ func handleSetQueryRegister(s xmpp.Sender, iq *stanza.IQ, query *extensions.Quer
|
||||||
|
|
||||||
_, toOk, _ := toToID(iq.To)
|
_, toOk, _ := toToID(iq.To)
|
||||||
if toOk {
|
if toOk {
|
||||||
iqAnswerSetError(answer, query, 400)
|
iqAnswerRegisterSetError(answer, query, 400)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if query.Remove != nil {
|
if query.Remove != nil {
|
||||||
iqAnswerSetError(answer, query, 405)
|
iqAnswerRegisterSetError(answer, query, 405)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1174,7 +1179,7 @@ func handleSetQueryRegister(s xmpp.Sender, iq *stanza.IQ, query *extensions.Quer
|
||||||
if !ok {
|
if !ok {
|
||||||
session, ok = getTelegramInstance(bare, &persistence.Session{}, component)
|
session, ok = getTelegramInstance(bare, &persistence.Session{}, component)
|
||||||
if !ok {
|
if !ok {
|
||||||
iqAnswerSetError(answer, query, 500)
|
iqAnswerRegisterSetError(answer, query, 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1182,23 +1187,23 @@ func handleSetQueryRegister(s xmpp.Sender, iq *stanza.IQ, query *extensions.Quer
|
||||||
err := session.TryLogin(resource, query.Username)
|
err := session.TryLogin(resource, query.Username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err.Error() == telegram.TelegramAuthDone {
|
if err.Error() == telegram.TelegramAuthDone {
|
||||||
iqAnswerSetError(answer, query, 406)
|
iqAnswerRegisterSetError(answer, query, 406)
|
||||||
} else {
|
} else {
|
||||||
iqAnswerSetError(answer, query, 500)
|
iqAnswerRegisterSetError(answer, query, 500)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = session.SetPhoneNumber(query.Username)
|
err = session.SetPhoneNumber(query.Username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
iqAnswerSetError(answer, query, 500)
|
iqAnswerRegisterSetError(answer, query, 500)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// everything okay, the response should be empty with no payload/error at this point
|
// everything okay, the response should be empty with no payload/error at this point
|
||||||
gateway.SubscribeToTransport(component, iq.From)
|
gateway.SubscribeToTransport(component, iq.From)
|
||||||
} else {
|
} else {
|
||||||
iqAnswerSetError(answer, query, 406)
|
iqAnswerRegisterSetError(answer, query, 406)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1590,51 +1595,108 @@ func handleSetQueryCommand(s xmpp.Sender, iq *stanza.IQ, command *stanza.Command
|
||||||
log.Debugf("command response: %#v %v", answer.Payload, cancelSend)
|
log.Debugf("command response: %#v %v", answer.Payload, cancelSend)
|
||||||
}
|
}
|
||||||
|
|
||||||
func iqAnswerSetError(answer *stanza.IQ, payload *extensions.QueryRegister, code int) {
|
func handleSetQueryMucAdmin(s xmpp.Sender, iq *stanza.IQ, query *extensions.QueryMucAdmin) {
|
||||||
answer.Type = stanza.IQTypeError
|
component, answer, ok := iqResultStub(s, iq)
|
||||||
|
if !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer gateway.ResumableSend(component, answer)
|
||||||
|
|
||||||
|
if query.Item.Role == "none" {
|
||||||
|
bare, _, fromOk := gateway.SplitJID(iq.From)
|
||||||
|
if !fromOk {
|
||||||
|
iqAnswerSetError(answer, 400)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
session, sessionOk := sessions[bare]
|
||||||
|
if !sessionOk || !session.Session.MUC {
|
||||||
|
iqAnswerSetError(answer, 401)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
toID, toOk, toIsGroup := toToID(iq.To)
|
||||||
|
if !toOk || !toIsGroup {
|
||||||
|
iqAnswerSetError(answer, 406)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
userID := session.GetMUCMemberIdByNickname(toID, query.Item.Nick)
|
||||||
|
if userID == 0 {
|
||||||
|
iqAnswerSetError(answer, 404)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
err := session.Kick(toID, userID, query.Item.Nick)
|
||||||
|
if err != nil {
|
||||||
|
iqAnswerSetError(answer, 500)
|
||||||
|
answer.Error.Text = err.Error()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func iqAnswerSetError(answer *stanza.IQ, code int) {
|
||||||
|
iqAnswerSetErrorInternal(answer, code, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func iqAnswerRegisterSetError(answer *stanza.IQ, payload *extensions.QueryRegister, code int) {
|
||||||
answer.Payload = *payload
|
answer.Payload = *payload
|
||||||
|
iqAnswerSetErrorInternal(answer, code, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func iqAnswerSetErrorInternal(answer *stanza.IQ, code int, registerMode bool) {
|
||||||
|
answer.Type = stanza.IQTypeError
|
||||||
switch code {
|
switch code {
|
||||||
case 400:
|
case 400:
|
||||||
answer.Error = &stanza.Err{
|
answer.Error = &stanza.Err{
|
||||||
Code: code,
|
|
||||||
Type: stanza.ErrorTypeModify,
|
Type: stanza.ErrorTypeModify,
|
||||||
Reason: "bad-request",
|
Reason: "bad-request",
|
||||||
}
|
}
|
||||||
|
case 401:
|
||||||
|
answer.Error = &stanza.Err{
|
||||||
|
Type: stanza.ErrorTypeAuth,
|
||||||
|
Reason: "not-authorized",
|
||||||
|
}
|
||||||
case 404:
|
case 404:
|
||||||
answer.Error = &stanza.Err{
|
answer.Error = &stanza.Err{
|
||||||
Code: code,
|
|
||||||
Type: stanza.ErrorTypeCancel,
|
Type: stanza.ErrorTypeCancel,
|
||||||
Reason: "item-not-found",
|
Reason: "item-not-found",
|
||||||
Text: "No such room",
|
|
||||||
}
|
}
|
||||||
case 405:
|
case 405:
|
||||||
answer.Error = &stanza.Err{
|
answer.Error = &stanza.Err{
|
||||||
Code: code,
|
|
||||||
Type: stanza.ErrorTypeCancel,
|
Type: stanza.ErrorTypeCancel,
|
||||||
Reason: "not-allowed",
|
Reason: "not-allowed",
|
||||||
Text: "Logging out is dangerous. If you are sure you would be able to receive the authentication code again, issue the /logout command to the transport",
|
|
||||||
}
|
}
|
||||||
case 406:
|
case 406:
|
||||||
answer.Error = &stanza.Err{
|
answer.Error = &stanza.Err{
|
||||||
Code: code,
|
|
||||||
Type: stanza.ErrorTypeModify,
|
Type: stanza.ErrorTypeModify,
|
||||||
Reason: "not-acceptable",
|
Reason: "not-acceptable",
|
||||||
Text: "Phone number already provided, chat with the transport for further instruction",
|
|
||||||
}
|
}
|
||||||
case 500:
|
case 500:
|
||||||
answer.Error = &stanza.Err{
|
answer.Error = &stanza.Err{
|
||||||
Code: code,
|
|
||||||
Type: stanza.ErrorTypeWait,
|
Type: stanza.ErrorTypeWait,
|
||||||
Reason: "internal-server-error",
|
Reason: "internal-server-error",
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
log.Error("Unknown error code, falling back with empty reason")
|
log.Error("Unknown error code, falling back with empty reason")
|
||||||
answer.Error = &stanza.Err{
|
answer.Error = &stanza.Err{
|
||||||
Code: code,
|
|
||||||
Type: stanza.ErrorTypeCancel,
|
Type: stanza.ErrorTypeCancel,
|
||||||
Reason: "undefined-condition",
|
Reason: "undefined-condition",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
answer.Error.Code = code
|
||||||
|
|
||||||
|
if registerMode {
|
||||||
|
switch code {
|
||||||
|
case 404:
|
||||||
|
answer.Error.Text = "No such room"
|
||||||
|
case 405:
|
||||||
|
answer.Error.Text = "Logging out is dangerous. If you are sure you would be able to receive the authentication code again, issue the /logout command to the transport"
|
||||||
|
case 406:
|
||||||
|
answer.Error.Text = "Phone number already provided, chat with the transport for further instruction"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func presenceReplySetError(reply *stanza.Presence, code int) {
|
func presenceReplySetError(reply *stanza.Presence, code int) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue