Mercurial > libervia-backend
comparison sat/plugins/plugin_xep_0085.py @ 4037:524856bd7b19
massive refactoring to switch from camelCase to snake_case:
historically, Libervia (SàT before) was using camelCase as allowed by PEP8 when using a
pre-PEP8 code, to use the same coding style as in Twisted.
However, snake_case is more readable and it's better to follow PEP8 best practices, so it
has been decided to move on full snake_case. Because Libervia has a huge codebase, this
ended with a ugly mix of camelCase and snake_case.
To fix that, this patch does a big refactoring by renaming every function and method
(including bridge) that are not coming from Twisted or Wokkel, to use fully snake_case.
This is a massive change, and may result in some bugs.
author | Goffi <goffi@goffi.org> |
---|---|
date | Sat, 08 Apr 2023 13:54:42 +0200 |
parents | 6cf4bd6972c2 |
children | c23cad65ae99 |
comparison
equal
deleted
inserted
replaced
4036:c4464d7ae97b | 4037:524856bd7b19 |
---|---|
101 log.info(_("Chat State Notifications plugin initialization")) | 101 log.info(_("Chat State Notifications plugin initialization")) |
102 self.host = host | 102 self.host = host |
103 self.map = {} # FIXME: would be better to use client instead of mapping profile to data | 103 self.map = {} # FIXME: would be better to use client instead of mapping profile to data |
104 | 104 |
105 # parameter value is retrieved before each use | 105 # parameter value is retrieved before each use |
106 host.memory.updateParams(self.params) | 106 host.memory.update_params(self.params) |
107 | 107 |
108 # triggers from core | 108 # triggers from core |
109 host.trigger.add("messageReceived", self.messageReceivedTrigger) | 109 host.trigger.add("messageReceived", self.message_received_trigger) |
110 host.trigger.add("sendMessage", self.sendMessageTrigger) | 110 host.trigger.add("sendMessage", self.send_message_trigger) |
111 host.trigger.add("paramUpdateTrigger", self.paramUpdateTrigger) | 111 host.trigger.add("param_update_trigger", self.param_update_trigger) |
112 | 112 |
113 # args: to_s (jid as string), profile | 113 # args: to_s (jid as string), profile |
114 host.bridge.addMethod( | 114 host.bridge.add_method( |
115 "chatStateComposing", | 115 "chat_state_composing", |
116 ".plugin", | 116 ".plugin", |
117 in_sign="ss", | 117 in_sign="ss", |
118 out_sign="", | 118 out_sign="", |
119 method=self.chatStateComposing, | 119 method=self.chat_state_composing, |
120 ) | 120 ) |
121 | 121 |
122 # args: from (jid as string), state in CHAT_STATES, profile | 122 # args: from (jid as string), state in CHAT_STATES, profile |
123 host.bridge.addSignal("chatStateReceived", ".plugin", signature="sss") | 123 host.bridge.add_signal("chat_state_received", ".plugin", signature="sss") |
124 | 124 |
125 def getHandler(self, client): | 125 def get_handler(self, client): |
126 return XEP_0085_handler(self, client.profile) | 126 return XEP_0085_handler(self, client.profile) |
127 | 127 |
128 def profileDisconnected(self, client): | 128 def profile_disconnected(self, client): |
129 """Eventually send a 'gone' state to all one2one contacts.""" | 129 """Eventually send a 'gone' state to all one2one contacts.""" |
130 profile = client.profile | 130 profile = client.profile |
131 if profile not in self.map: | 131 if profile not in self.map: |
132 return | 132 return |
133 for to_jid in self.map[profile]: | 133 for to_jid in self.map[profile]: |
135 # before the chat state, so it will be ignored... find a way to | 135 # before the chat state, so it will be ignored... find a way to |
136 # actually defer the disconnection | 136 # actually defer the disconnection |
137 self.map[profile][to_jid]._onEvent("gone") | 137 self.map[profile][to_jid]._onEvent("gone") |
138 del self.map[profile] | 138 del self.map[profile] |
139 | 139 |
140 def updateCache(self, entity_jid, value, profile): | 140 def update_cache(self, entity_jid, value, profile): |
141 """Update the entity data of the given profile for one or all contacts. | 141 """Update the entity data of the given profile for one or all contacts. |
142 Reset the chat state(s) display if the notification has been disabled. | 142 Reset the chat state(s) display if the notification has been disabled. |
143 | 143 |
144 @param entity_jid: contact's JID, or C.ENTITY_ALL to update all contacts. | 144 @param entity_jid: contact's JID, or C.ENTITY_ALL to update all contacts. |
145 @param value: True, False or DELETE_VALUE to delete the entity data | 145 @param value: True, False or DELETE_VALUE to delete the entity data |
146 @param profile: current profile | 146 @param profile: current profile |
147 """ | 147 """ |
148 client = self.host.getClient(profile) | 148 client = self.host.get_client(profile) |
149 if value == DELETE_VALUE: | 149 if value == DELETE_VALUE: |
150 self.host.memory.delEntityDatum(client, entity_jid, ENTITY_KEY) | 150 self.host.memory.del_entity_datum(client, entity_jid, ENTITY_KEY) |
151 else: | 151 else: |
152 self.host.memory.updateEntityData( | 152 self.host.memory.update_entity_data( |
153 client, entity_jid, ENTITY_KEY, value | 153 client, entity_jid, ENTITY_KEY, value |
154 ) | 154 ) |
155 if not value or value == DELETE_VALUE: | 155 if not value or value == DELETE_VALUE: |
156 # reinit chat state UI for this or these contact(s) | 156 # reinit chat state UI for this or these contact(s) |
157 self.host.bridge.chatStateReceived(entity_jid.full(), "", profile) | 157 self.host.bridge.chat_state_received(entity_jid.full(), "", profile) |
158 | 158 |
159 def paramUpdateTrigger(self, name, value, category, type_, profile): | 159 def param_update_trigger(self, name, value, category, type_, profile): |
160 """Reset all the existing chat state entity data associated with this profile after a parameter modification. | 160 """Reset all the existing chat state entity data associated with this profile after a parameter modification. |
161 | 161 |
162 @param name: parameter name | 162 @param name: parameter name |
163 @param value: "true" to activate the notifications, or any other value to delete it | 163 @param value: "true" to activate the notifications, or any other value to delete it |
164 @param category: parameter category | 164 @param category: parameter category |
165 @param type_: parameter type | 165 @param type_: parameter type |
166 """ | 166 """ |
167 if (category, name) == (PARAM_KEY, PARAM_NAME): | 167 if (category, name) == (PARAM_KEY, PARAM_NAME): |
168 self.updateCache( | 168 self.update_cache( |
169 C.ENTITY_ALL, True if C.bool(value) else DELETE_VALUE, profile=profile | 169 C.ENTITY_ALL, True if C.bool(value) else DELETE_VALUE, profile=profile |
170 ) | 170 ) |
171 return False | 171 return False |
172 return True | 172 return True |
173 | 173 |
174 def messageReceivedTrigger(self, client, message, post_treat): | 174 def message_received_trigger(self, client, message, post_treat): |
175 """ | 175 """ |
176 Update the entity cache when we receive a message with body. | 176 Update the entity cache when we receive a message with body. |
177 Check for a chat state in the message and signal frontends. | 177 Check for a chat state in the message and signal frontends. |
178 """ | 178 """ |
179 profile = client.profile | 179 profile = client.profile |
180 if not self.host.memory.getParamA(PARAM_NAME, PARAM_KEY, profile_key=profile): | 180 if not self.host.memory.param_get_a(PARAM_NAME, PARAM_KEY, profile_key=profile): |
181 return True | 181 return True |
182 | 182 |
183 from_jid = JID(message.getAttribute("from")) | 183 from_jid = JID(message.getAttribute("from")) |
184 if self._isMUC(from_jid, profile): | 184 if self._is_muc(from_jid, profile): |
185 from_jid = from_jid.userhostJID() | 185 from_jid = from_jid.userhostJID() |
186 else: # update entity data for one2one chat | 186 else: # update entity data for one2one chat |
187 # assert from_jid.resource # FIXME: assert doesn't work on normal message from server (e.g. server announce), because there is no resource | 187 # assert from_jid.resource # FIXME: assert doesn't work on normal message from server (e.g. server announce), because there is no resource |
188 try: | 188 try: |
189 next(domish.generateElementsNamed(message.elements(), name="body")) | 189 next(domish.generateElementsNamed(message.elements(), name="body")) |
190 try: | 190 try: |
191 next(domish.generateElementsNamed(message.elements(), name="active")) | 191 next(domish.generateElementsNamed(message.elements(), name="active")) |
192 # contact enabled Chat State Notifications | 192 # contact enabled Chat State Notifications |
193 self.updateCache(from_jid, True, profile=profile) | 193 self.update_cache(from_jid, True, profile=profile) |
194 except StopIteration: | 194 except StopIteration: |
195 if message.getAttribute("type") == "chat": | 195 if message.getAttribute("type") == "chat": |
196 # contact didn't enable Chat State Notifications | 196 # contact didn't enable Chat State Notifications |
197 self.updateCache(from_jid, False, profile=profile) | 197 self.update_cache(from_jid, False, profile=profile) |
198 return True | 198 return True |
199 except StopIteration: | 199 except StopIteration: |
200 pass | 200 pass |
201 | 201 |
202 # send our next "composing" states to any MUC and to the contacts who enabled the feature | 202 # send our next "composing" states to any MUC and to the contacts who enabled the feature |
203 self._chatStateInit(from_jid, message.getAttribute("type"), profile) | 203 self._chat_state_init(from_jid, message.getAttribute("type"), profile) |
204 | 204 |
205 state_list = [ | 205 state_list = [ |
206 child.name | 206 child.name |
207 for child in message.elements() | 207 for child in message.elements() |
208 if message.getAttribute("type") in MESSAGE_TYPES | 208 if message.getAttribute("type") in MESSAGE_TYPES |
210 and child.defaultUri == NS_CHAT_STATES | 210 and child.defaultUri == NS_CHAT_STATES |
211 ] | 211 ] |
212 for state in state_list: | 212 for state in state_list: |
213 # there must be only one state according to the XEP | 213 # there must be only one state according to the XEP |
214 if state != "gone" or message.getAttribute("type") != "groupchat": | 214 if state != "gone" or message.getAttribute("type") != "groupchat": |
215 self.host.bridge.chatStateReceived( | 215 self.host.bridge.chat_state_received( |
216 message.getAttribute("from"), state, profile | 216 message.getAttribute("from"), state, profile |
217 ) | 217 ) |
218 break | 218 break |
219 return True | 219 return True |
220 | 220 |
221 def sendMessageTrigger( | 221 def send_message_trigger( |
222 self, client, mess_data, pre_xml_treatments, post_xml_treatments | 222 self, client, mess_data, pre_xml_treatments, post_xml_treatments |
223 ): | 223 ): |
224 """ | 224 """ |
225 Eventually add the chat state to the message and initiate | 225 Eventually add the chat state to the message and initiate |
226 the state machine when sending an "active" state. | 226 the state machine when sending an "active" state. |
228 profile = client.profile | 228 profile = client.profile |
229 | 229 |
230 def treatment(mess_data): | 230 def treatment(mess_data): |
231 message = mess_data["xml"] | 231 message = mess_data["xml"] |
232 to_jid = JID(message.getAttribute("to")) | 232 to_jid = JID(message.getAttribute("to")) |
233 if not self._checkActivation(to_jid, forceEntityData=True, profile=profile): | 233 if not self._check_activation(to_jid, forceEntityData=True, profile=profile): |
234 return mess_data | 234 return mess_data |
235 try: | 235 try: |
236 # message with a body always mean active state | 236 # message with a body always mean active state |
237 next(domish.generateElementsNamed(message.elements(), name="body")) | 237 next(domish.generateElementsNamed(message.elements(), name="body")) |
238 message.addElement("active", NS_CHAT_STATES) | 238 message.addElement("active", NS_CHAT_STATES) |
239 # launch the chat state machine (init the timer) | 239 # launch the chat state machine (init the timer) |
240 if self._isMUC(to_jid, profile): | 240 if self._is_muc(to_jid, profile): |
241 to_jid = to_jid.userhostJID() | 241 to_jid = to_jid.userhostJID() |
242 self._chatStateActive(to_jid, mess_data["type"], profile) | 242 self._chat_state_active(to_jid, mess_data["type"], profile) |
243 except StopIteration: | 243 except StopIteration: |
244 if "chat_state" in mess_data["extra"]: | 244 if "chat_state" in mess_data["extra"]: |
245 state = mess_data["extra"].pop("chat_state") | 245 state = mess_data["extra"].pop("chat_state") |
246 assert state in CHAT_STATES | 246 assert state in CHAT_STATES |
247 message.addElement(state, NS_CHAT_STATES) | 247 message.addElement(state, NS_CHAT_STATES) |
248 return mess_data | 248 return mess_data |
249 | 249 |
250 post_xml_treatments.addCallback(treatment) | 250 post_xml_treatments.addCallback(treatment) |
251 return True | 251 return True |
252 | 252 |
253 def _isMUC(self, to_jid, profile): | 253 def _is_muc(self, to_jid, profile): |
254 """Tell if that JID is a MUC or not | 254 """Tell if that JID is a MUC or not |
255 | 255 |
256 @param to_jid (JID): full or bare JID to check | 256 @param to_jid (JID): full or bare JID to check |
257 @param profile (str): %(doc_profile)s | 257 @param profile (str): %(doc_profile)s |
258 @return: bool | 258 @return: bool |
259 """ | 259 """ |
260 client = self.host.getClient(profile) | 260 client = self.host.get_client(profile) |
261 try: | 261 try: |
262 type_ = self.host.memory.getEntityDatum( | 262 type_ = self.host.memory.get_entity_datum( |
263 client, to_jid.userhostJID(), C.ENTITY_TYPE) | 263 client, to_jid.userhostJID(), C.ENTITY_TYPE) |
264 if type_ == C.ENTITY_TYPE_MUC: | 264 if type_ == C.ENTITY_TYPE_MUC: |
265 return True | 265 return True |
266 except (exceptions.UnknownEntityError, KeyError): | 266 except (exceptions.UnknownEntityError, KeyError): |
267 pass | 267 pass |
268 return False | 268 return False |
269 | 269 |
270 def _checkActivation(self, to_jid, forceEntityData, profile): | 270 def _check_activation(self, to_jid, forceEntityData, profile): |
271 """ | 271 """ |
272 @param to_jid: the contact's full JID (or bare if you know it's a MUC) | 272 @param to_jid: the contact's full JID (or bare if you know it's a MUC) |
273 @param forceEntityData: if set to True, a non-existing | 273 @param forceEntityData: if set to True, a non-existing |
274 entity data will be considered to be True (and initialized) | 274 entity data will be considered to be True (and initialized) |
275 @param: current profile | 275 @param: current profile |
276 @return: True if the notifications should be sent to this JID. | 276 @return: True if the notifications should be sent to this JID. |
277 """ | 277 """ |
278 client = self.host.getClient(profile) | 278 client = self.host.get_client(profile) |
279 # check if the parameter is active | 279 # check if the parameter is active |
280 if not self.host.memory.getParamA(PARAM_NAME, PARAM_KEY, profile_key=profile): | 280 if not self.host.memory.param_get_a(PARAM_NAME, PARAM_KEY, profile_key=profile): |
281 return False | 281 return False |
282 # check if notifications should be sent to this contact | 282 # check if notifications should be sent to this contact |
283 if self._isMUC(to_jid, profile): | 283 if self._is_muc(to_jid, profile): |
284 return True | 284 return True |
285 # FIXME: this assertion crash when we want to send a message to an online bare jid | 285 # FIXME: this assertion crash when we want to send a message to an online bare jid |
286 # assert to_jid.resource or not self.host.memory.isEntityAvailable(to_jid, profile) # must either have a resource, or talk to an offline contact | 286 # assert to_jid.resource or not self.host.memory.is_entity_available(to_jid, profile) # must either have a resource, or talk to an offline contact |
287 try: | 287 try: |
288 return self.host.memory.getEntityDatum(client, to_jid, ENTITY_KEY) | 288 return self.host.memory.get_entity_datum(client, to_jid, ENTITY_KEY) |
289 except (exceptions.UnknownEntityError, KeyError): | 289 except (exceptions.UnknownEntityError, KeyError): |
290 if forceEntityData: | 290 if forceEntityData: |
291 # enable it for the first time | 291 # enable it for the first time |
292 self.updateCache(to_jid, True, profile=profile) | 292 self.update_cache(to_jid, True, profile=profile) |
293 return True | 293 return True |
294 # wait for the first message before sending states | 294 # wait for the first message before sending states |
295 return False | 295 return False |
296 | 296 |
297 def _chatStateInit(self, to_jid, mess_type, profile): | 297 def _chat_state_init(self, to_jid, mess_type, profile): |
298 """ | 298 """ |
299 Data initialization for the chat state machine. | 299 Data initialization for the chat state machine. |
300 | 300 |
301 @param to_jid (JID): full JID for one2one, bare JID for MUC | 301 @param to_jid (JID): full JID for one2one, bare JID for MUC |
302 @param mess_type (str): "one2one" or "groupchat" | 302 @param mess_type (str): "one2one" or "groupchat" |
307 profile_map = self.map.setdefault(profile, {}) | 307 profile_map = self.map.setdefault(profile, {}) |
308 if to_jid not in profile_map: | 308 if to_jid not in profile_map: |
309 machine = ChatStateMachine(self.host, to_jid, mess_type, profile) | 309 machine = ChatStateMachine(self.host, to_jid, mess_type, profile) |
310 self.map[profile][to_jid] = machine | 310 self.map[profile][to_jid] = machine |
311 | 311 |
312 def _chatStateActive(self, to_jid, mess_type, profile_key): | 312 def _chat_state_active(self, to_jid, mess_type, profile_key): |
313 """ | 313 """ |
314 Launch the chat state machine on "active" state. | 314 Launch the chat state machine on "active" state. |
315 | 315 |
316 @param to_jid (JID): full JID for one2one, bare JID for MUC | 316 @param to_jid (JID): full JID for one2one, bare JID for MUC |
317 @param mess_type (str): "one2one" or "groupchat" | 317 @param mess_type (str): "one2one" or "groupchat" |
318 @param profile (str): %(doc_profile)s | 318 @param profile (str): %(doc_profile)s |
319 """ | 319 """ |
320 profile = self.host.memory.getProfileName(profile_key) | 320 profile = self.host.memory.get_profile_name(profile_key) |
321 if profile is None: | 321 if profile is None: |
322 raise exceptions.ProfileUnknownError | 322 raise exceptions.ProfileUnknownError |
323 self._chatStateInit(to_jid, mess_type, profile) | 323 self._chat_state_init(to_jid, mess_type, profile) |
324 self.map[profile][to_jid]._onEvent("active") | 324 self.map[profile][to_jid]._onEvent("active") |
325 | 325 |
326 def chatStateComposing(self, to_jid_s, profile_key): | 326 def chat_state_composing(self, to_jid_s, profile_key): |
327 """Move to the "composing" state when required. | 327 """Move to the "composing" state when required. |
328 | 328 |
329 Since this method is called from the front-end, it needs to check the | 329 Since this method is called from the front-end, it needs to check the |
330 values of the parameter "Send chat state notifications" and the entity | 330 values of the parameter "Send chat state notifications" and the entity |
331 data associated to the target JID. | 331 data associated to the target JID. |
332 | 332 |
333 @param to_jid_s (str): contact full JID as a string | 333 @param to_jid_s (str): contact full JID as a string |
334 @param profile_key (str): %(doc_profile_key)s | 334 @param profile_key (str): %(doc_profile_key)s |
335 """ | 335 """ |
336 # TODO: try to optimize this method which is called often | 336 # TODO: try to optimize this method which is called often |
337 client = self.host.getClient(profile_key) | 337 client = self.host.get_client(profile_key) |
338 to_jid = JID(to_jid_s) | 338 to_jid = JID(to_jid_s) |
339 if self._isMUC(to_jid, client.profile): | 339 if self._is_muc(to_jid, client.profile): |
340 to_jid = to_jid.userhostJID() | 340 to_jid = to_jid.userhostJID() |
341 elif not to_jid.resource: | 341 elif not to_jid.resource: |
342 to_jid.resource = self.host.memory.getMainResource(client, to_jid) | 342 to_jid.resource = self.host.memory.main_resource_get(client, to_jid) |
343 if not self._checkActivation( | 343 if not self._check_activation( |
344 to_jid, forceEntityData=False, profile=client.profile | 344 to_jid, forceEntityData=False, profile=client.profile |
345 ): | 345 ): |
346 return | 346 return |
347 try: | 347 try: |
348 self.map[client.profile][to_jid]._onEvent("composing") | 348 self.map[client.profile][to_jid]._onEvent("composing") |
390 log.debug( | 390 log.debug( |
391 "sending state '{state}' to {jid}".format( | 391 "sending state '{state}' to {jid}".format( |
392 state=state, jid=self.to_jid.full() | 392 state=state, jid=self.to_jid.full() |
393 ) | 393 ) |
394 ) | 394 ) |
395 client = self.host.getClient(self.profile) | 395 client = self.host.get_client(self.profile) |
396 mess_data = { | 396 mess_data = { |
397 "from": client.jid, | 397 "from": client.jid, |
398 "to": self.to_jid, | 398 "to": self.to_jid, |
399 "uid": "", | 399 "uid": "", |
400 "message": {}, | 400 "message": {}, |
401 "type": self.mess_type, | 401 "type": self.mess_type, |
402 "subject": {}, | 402 "subject": {}, |
403 "extra": {}, | 403 "extra": {}, |
404 } | 404 } |
405 client.generateMessageXML(mess_data) | 405 client.generate_message_xml(mess_data) |
406 mess_data["xml"].addElement(state, NS_CHAT_STATES) | 406 mess_data["xml"].addElement(state, NS_CHAT_STATES) |
407 client.send(mess_data["xml"]) | 407 client.send(mess_data["xml"]) |
408 | 408 |
409 self.state = state | 409 self.state = state |
410 try: | 410 try: |