Coverage for slidge / core / mixins / disco.py: 97%

86 statements  

« prev     ^ index     » next       coverage.py v7.13.0, created at 2026-02-15 09:02 +0000

1from slixmpp.plugins.xep_0004 import Form 

2from slixmpp.plugins.xep_0030.stanza.info import DiscoInfo 

3from slixmpp.types import OptJid 

4 

5from .base import Base 

6 

7 

8class BaseDiscoMixin(Base): 

9 DISCO_TYPE: str = NotImplemented 

10 DISCO_CATEGORY: str = NotImplemented 

11 DISCO_NAME: str = NotImplemented 

12 DISCO_LANG = None 

13 

14 def _get_disco_name(self) -> str | None: 

15 if self.DISCO_NAME is NotImplemented: 

16 return self.xmpp.COMPONENT_NAME 

17 return self.DISCO_NAME or self.xmpp.COMPONENT_NAME 

18 

19 def features(self): 

20 return [] 

21 

22 async def extended_features(self) -> list[Form] | None: 

23 return None 

24 

25 async def get_disco_info(self, jid: OptJid = None, node: str | None = None): 

26 info = DiscoInfo() 

27 for feature in self.features(): 

28 info.add_feature(feature) 

29 info.add_identity( 

30 category=self.DISCO_CATEGORY, 

31 itype=self.DISCO_TYPE, 

32 name=self._get_disco_name(), 

33 lang=self.DISCO_LANG, 

34 ) 

35 if forms := await self.extended_features(): 

36 for form in forms: 

37 info.append(form) 

38 return info 

39 

40 async def get_caps_ver(self, jid: OptJid = None, node: str | None = None): 

41 info = await self.get_disco_info(jid, node) 

42 caps = self.xmpp.plugin["xep_0115"] 

43 ver = caps.generate_verstring(info, caps.hash) 

44 return ver 

45 

46 

47class ChatterDiscoMixin(BaseDiscoMixin): 

48 AVATAR = True 

49 RECEIPTS = True 

50 MARKS = True 

51 CHAT_STATES = True 

52 UPLOAD = True 

53 CORRECTION = True 

54 REACTION = True 

55 RETRACTION = True 

56 REPLIES = True 

57 INVITATION_RECIPIENT = False 

58 

59 DISCO_TYPE = "pc" 

60 DISCO_CATEGORY = "client" 

61 DISCO_NAME = "" 

62 

63 def features(self): 

64 features = [] 

65 if self.CHAT_STATES: 

66 features.append("http://jabber.org/protocol/chatstates") 

67 if self.RECEIPTS: 

68 features.append("urn:xmpp:receipts") 

69 if self.CORRECTION: 

70 features.append("urn:xmpp:message-correct:0") 

71 if self.MARKS: 

72 features.append("urn:xmpp:chat-markers:0") 

73 if self.UPLOAD: 

74 features.append("jabber:x:oob") 

75 if self.REACTION: 

76 features.append("urn:xmpp:reactions:0") 

77 if self.RETRACTION: 

78 features.append("urn:xmpp:message-retract:0") 

79 if self.REPLIES: 

80 features.append("urn:xmpp:reply:0") 

81 if self.INVITATION_RECIPIENT: 

82 features.append("jabber:x:conference") 

83 features.append("urn:ietf:params:xml:ns:vcard-4.0") 

84 return features 

85 

86 async def extended_features(self): 

87 f = getattr(self, "restricted_emoji_extended_feature", None) 

88 if f is None: 

89 return 

90 

91 e = await f() 

92 if not e: 

93 return 

94 

95 return [e] 

96 

97 

98class ContactAccountDiscoMixin(BaseDiscoMixin): 

99 async def get_disco_info(self, jid: OptJid = None, node: str | None = None): 

100 if jid and jid.resource: 

101 return await super().get_disco_info() 

102 info = DiscoInfo() 

103 info.add_feature("http://jabber.org/protocol/pubsub") 

104 info.add_feature("http://jabber.org/protocol/pubsub#retrieve-items") 

105 info.add_feature("http://jabber.org/protocol/pubsub#subscribe") 

106 info.add_identity( 

107 category="account", 

108 itype="registered", 

109 name=self._get_disco_name(), 

110 lang=self.DISCO_LANG, 

111 ) 

112 info.add_identity( 

113 category="pubsub", 

114 itype="pep", 

115 name=self._get_disco_name(), 

116 lang=self.DISCO_LANG, 

117 ) 

118 return info