From 749933fc16005d8e81d54830f532dd1cb9f85773 Mon Sep 17 00:00:00 2001 From: Florent Le Coz Date: Tue, 11 Jan 2011 06:43:31 +0100 Subject: /accept and /deny commands User can now decide to accept or deny a subscription, in the roster --- src/connection.py | 1 + src/core.py | 18 ++++++++++- src/roster.py | 18 +++++++++-- src/tabs.py | 91 ++++++++++++++++++++++++++++++++++++++----------------- src/windows.py | 9 ++++-- 5 files changed, 103 insertions(+), 34 deletions(-) (limited to 'src') diff --git a/src/connection.py b/src/connection.py index dc2eeaae..d29fec82 100644 --- a/src/connection.py +++ b/src/connection.py @@ -50,6 +50,7 @@ class Connection(sleekxmpp.ClientXMPP): password = None sleekxmpp.ClientXMPP.__init__(self, jid, password, ssl=True) self.auto_reconnect = False + self.auto_authorize = None self.register_plugin('xep_0030') self.register_plugin('xep_0045') if config.get('send_poezio_info', 'true') == 'true': diff --git a/src/core.py b/src/core.py index 6fdb883c..c3b3510d 100644 --- a/src/core.py +++ b/src/core.py @@ -159,7 +159,7 @@ class Core(object): self.xmpp.add_event_handler("got_offline" , self.on_got_offline) self.xmpp.add_event_handler("roster_update", self.on_roster_update) self.xmpp.add_event_handler("changed_status", self.on_presence) - + self.xmpp.add_event_handler("changed_subscription", self.on_changed_subscription) self.information(_('Welcome to poezio!')) self.refresh_window() @@ -594,6 +594,22 @@ class Core(object): if isinstance(self.current_tab(), tabs.RosterInfoTab): self.refresh_window() + def on_changed_subscription(self, presence): + """ + Triggered whenever a presence stanza with a type of subscribe, subscribed, unsubscribe, or unsubscribed is received. + """ + if presence['type'] == 'subscribe': + jid = presence['from'].bare + contact = roster.get_contact_by_jid(jid) + if not contact: + contact = Contact(jid) + roster.add_contact(contact, jid) + roster.edit_groups_of_contact(contact, []) + contact.set_ask('asked') + if isinstance(self.current_tab(), tabs.RosterInfoTab): + self.refresh_window() + + def full_screen_redraw(self): """ Completely erase and redraw the screen diff --git a/src/roster.py b/src/roster.py index 1abc1cd3..1fd1099d 100644 --- a/src/roster.py +++ b/src/roster.py @@ -37,9 +37,17 @@ class Roster(object): """ Add a contact to the contact list """ - assert jid not in self._contacts self._contacts[jid] = contact + def remove_contact(self, jid): + """ + Remove a contact from the contact list + """ + contact = self.get_contact_by_jid(jid) + for group in contact._groups: + group.remove_contact_from_group(contact) + del self._contacts[jid] + def get_contact_len(self): """ Return the number of contacts in this group @@ -110,6 +118,12 @@ class Roster(object): """ return self._roster_groups + def get_contacts(self): + """ + Return a list of all the contact + """ + return [contact for contact in self._contacts.values()] + def save_to_config_file(self): """ Save various information to the config file @@ -127,7 +141,7 @@ class Roster(object): """ length = 0 for group in self._roster_groups: - if group.get_nb_connected_contacts() == 0: + if config.get('roster_show_offline', 'false') == 'false' and group.get_nb_connected_contacts() == 0: continue length += 1 # One for the group's line itself if not group.folded: diff --git a/src/tabs.py b/src/tabs.py index c5c7d791..6396b833 100644 --- a/src/tabs.py +++ b/src/tabs.py @@ -94,6 +94,26 @@ class Tab(object): return True return False + def on_enter(self, provided_text=None): + """ + Execute the command in the input and return False if + the input didn't contain a command + """ + txt = provided_text or self.input.key_enter() + if txt.startswith('/') and not txt.startswith('//') and\ + not txt.startswith('/me '): + command = txt.strip().split()[0][1:] + arg = txt[2+len(command):] # jump the '/' and the ' ' + if command in self.core.commands: # check global commands + self.core.commands[command][0](arg) + elif command in self.commands: # check tab-specific commands + self.commands[command][0](arg) + else: + self.core.information(_("Unknown command (%s)") % (command), _('Error')) + return True + else: + return False + def resize(self): self.size = (self.height, self.width) = self.core.stdscr.getmaxyx() if self.height < MIN_HEIGHT or self.width < MIN_WIDTH: @@ -225,18 +245,7 @@ class ChatTab(Tab): self.input.auto_completion(words, ' ') def on_enter(self): - txt = self.input.key_enter() - if txt.startswith('/') and not txt.startswith('//') and\ - not txt.startswith('/me '): - command = txt.strip().split()[0][1:] - arg = txt[2+len(command):] # jump the '/' and the ' ' - if command in self.core.commands: # check global commands - self.core.commands[command][0](arg) - elif command in self.commands: # check tab-specific commands - self.commands[command][0](arg) - else: - self.core.information(_("Unknown command (%s)") % (command), _('Error')) - else: + if not Tab.on_enter(self): if txt.startswith('//'): txt = txt[1:] self.command_say(txt) @@ -277,20 +286,6 @@ class InfoTab(ChatTab): self.tab_win.refresh(tabs, tabs[0]) self.input.refresh() - def on_enter(self): - # TODO duplicate - txt = self.input.get_text() - if txt.startswith('/') and not txt.startswith('//') and\ - not txt.startswith('/me '): - command = txt.strip().split()[0][1:] - arg = txt[2+len(command):] # jump the '/' and the ' ' - if command in self.core.commands: # check global commands - self.core.commands[command][0](arg) - elif command in self.commands: # check tab-specific commands - self.commands[command][0](arg) - else: - self.core.information(_("Unknown command (%s)") % (command), _('Error')) - def completion(self): self.complete_commands(self.input) @@ -758,6 +753,7 @@ class RosterInfoTab(Tab): self.key_func["s"] = self.start_search self.key_func["S"] = self.start_search_slow self.commands['deny'] = (self.command_deny, _("Usage: /deny [jid]\nDeny: Use this command to remove and deny your presence to the provided JID (or the selected contact in your roster), who is asking you to be in his/here roster"), self.completion_deny) + self.commands['accept'] = (self.command_accept, _("Usage: /accpet [jid]\nAccept: Use this command to authorize the provided JID (or the selected contact in your roster), to see your presence, and to ask to subscribe to it (mutual presence subscription)."), self.completion_deny) self.resize() def resize(self): @@ -781,7 +777,46 @@ class RosterInfoTab(Tab): """ Denies a JID from our roster """ - + args = args.split() + if not args: + item = self.roster_win.selected_row + if isinstance(item, Contact) and item.get_ask() == 'asked': + jid = item.get_bare_jid() + else: + self.core.information('No subscription to deny') + return + else: + jid = args[0] + self.core.xmpp.sendPresence(pto=jid, ptype='unsubscribed') + if self.core.xmpp.update_roster(jid, subscription='remove'): + roster.remove_contact(jid) + + def completion_deny(self, the_input): + """ + Complete the first argument from the list of the + contact with ask=='subscribe' + """ + jids = [contact.get_bare_jid() for contact in roster.get_contacts()\ + if contact.get_ask() == 'asked'] + the_input.auto_completion(jids, '') + + def command_accept(self, args): + """ + Accept a JID from in roster. Authorize it AND subscribe to it + """ + args = args.split() + if not args: + item = self.roster_win.selected_row + if isinstance(item, Contact) and item.get_ask() == 'asked': + jid = item.get_bare_jid() + else: + self.core.information('No subscription to deny') + return + else: + jid = args[0] + self.core.xmpp.sendPresence(pto=jid, ptype='subscribed') + self.core.xmpp.sendPresence(pto=jid, ptype='subscribe') + def refresh(self, tabs, informations, roster): if not self.visible: return @@ -836,7 +871,7 @@ class RosterInfoTab(Tab): def execute_slash_command(self, txt): if txt.startswith('/'): - self.core.execute(txt) + Tab.on_enter(self, txt) return self.reset_help_message() def on_lose_focus(self): diff --git a/src/windows.py b/src/windows.py index 4020e72b..cbb58b3f 100644 --- a/src/windows.py +++ b/src/windows.py @@ -1187,7 +1187,7 @@ class RosterWin(Win): self.draw_roster_information(roster) y = 1 for group in roster.get_groups(): - if group.get_nb_connected_contacts() == 0: + if config.get('roster_show_offline', 'false') == 'false' and group.get_nb_connected_contacts() == 0: continue # Ignore empty groups # This loop is really REALLY ugly :^) if y-1 == self.pos: @@ -1283,7 +1283,7 @@ class RosterWin(Win): self.addstr(display_name, curses.color_pair(14)) else: self.addstr(display_name) - if contact.get_ask(): + if contact.get_ask() == 'asked': self.addstr('?', curses.color_pair(1)) def draw_resource_line(self, y, resource, colored): @@ -1324,7 +1324,10 @@ class ContactInfoWin(Win): self.finish_line(theme.COLOR_INFORMATION_BAR) self.addstr(1, 0, 'Subscription: %s' % (contact.get_subscription(),)) if contact.get_ask(): - self.addstr(' Ask: %s' % (contact.get_ask(),), curses.color_pair(1)) + if contact.get_ask() == 'asked': + self.addstr(' Ask: %s' % (contact.get_ask(),), curses.color_pair(1)) + else: + self.addstr(' Ask: %s' % (contact.get_ask(),)) def draw_group_info(self, group): """ -- cgit v1.2.3