summaryrefslogtreecommitdiff
path: root/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'plugins')
-rw-r--r--plugins/display_corrections.py2
-rw-r--r--plugins/embed.py12
-rwxr-xr-xplugins/qr.py178
-rw-r--r--plugins/reorder.py29
-rw-r--r--plugins/upload.py22
5 files changed, 223 insertions, 20 deletions
diff --git a/plugins/display_corrections.py b/plugins/display_corrections.py
index e9e8a2e4..99982ec9 100644
--- a/plugins/display_corrections.py
+++ b/plugins/display_corrections.py
@@ -43,7 +43,7 @@ class Plugin(BasePlugin):
messages = self.api.get_conversation_messages()
if not messages:
return None
- for message in messages[::-1]:
+ for message in reversed(messages):
if message.old_message:
if nb == 1:
return message
diff --git a/plugins/embed.py b/plugins/embed.py
index 9895a927..0c4a4a2a 100644
--- a/plugins/embed.py
+++ b/plugins/embed.py
@@ -28,14 +28,13 @@ class Plugin(BasePlugin):
help='Embed an image url into the contact\'s client',
usage='<image_url>')
- def embed_image_url(self, args):
+ def embed_image_url(self, url):
tab = self.api.current_tab()
message = self.core.xmpp.make_message(tab.jid)
- message['body'] = args
- message['oob']['url'] = args
- if isinstance(tab, tabs.MucTab):
- message['type'] = 'groupchat'
- else:
+ message['body'] = url
+ message['oob']['url'] = url
+ message['type'] = 'groupchat'
+ if not isinstance(tab, tabs.MucTab):
message['type'] = 'chat'
tab.add_message(
message['body'],
@@ -46,3 +45,4 @@ class Plugin(BasePlugin):
typ=1,
)
message.send()
+ self.core.refresh_window()
diff --git a/plugins/qr.py b/plugins/qr.py
new file mode 100755
index 00000000..25530248
--- /dev/null
+++ b/plugins/qr.py
@@ -0,0 +1,178 @@
+#!/usr/bin/env python3
+
+import io
+import logging
+import qrcode
+import sys
+
+from poezio import windows
+from poezio.tabs import Tab
+from poezio.common import safeJID
+from poezio.core.structs import Command
+from poezio.decorators import command_args_parser
+from poezio.plugin import BasePlugin
+from poezio.theming import get_theme, to_curses_attr
+from poezio.windows.base_wins import Win
+
+log = logging.getLogger(__name__)
+
+class QrWindow(Win):
+ __slots__ = ('qr', 'invert', 'inverted')
+
+ str_invert = " Invert "
+ str_close = " Close "
+
+ def __init__(self, qr: str) -> None:
+ self.qr = qr
+ self.invert = True
+ self.inverted = True
+
+ def refresh(self) -> None:
+ self._win.erase()
+ # draw QR code
+ code = qrcode.QRCode()
+ code.add_data(self.qr)
+ out = io.StringIO()
+ code.print_ascii(out, invert=self.inverted)
+ self.addstr(" " + self.qr + "\n")
+ self.addstr(out.getvalue(), to_curses_attr((15, 0)))
+ self.addstr(" ")
+
+ col = to_curses_attr(get_theme().COLOR_TAB_NORMAL)
+
+ if self.invert:
+ self.addstr(self.str_invert, col)
+ else:
+ self.addstr(self.str_invert)
+
+ self.addstr(" ")
+
+ if self.invert:
+ self.addstr(self.str_close)
+ else:
+ self.addstr(self.str_close, col)
+
+ self._refresh()
+
+ def toggle_choice(self) -> None:
+ self.invert = not self.invert
+
+ def engage(self) -> bool:
+ if self.invert:
+ self.inverted = not self.inverted
+ return False
+ else:
+ return True
+
+class QrTab(Tab):
+ plugin_commands = {} # type: Dict[str, Command]
+ plugin_keys = {} # type: Dict[str, Callable]
+
+ def __init__(self, core, qr):
+ Tab.__init__(self, core)
+ self.state = 'highlight'
+ self.text = qr
+ self.name = qr
+ self.topic_win = windows.Topic()
+ self.topic_win.set_message(qr)
+ self.qr_win = QrWindow(qr)
+ self.help_win = windows.HelpText(
+ "Choose with arrow keys and press enter")
+ self.key_func['^I'] = self.toggle_choice
+ self.key_func[' '] = self.toggle_choice
+ self.key_func['KEY_LEFT'] = self.toggle_choice
+ self.key_func['KEY_RIGHT'] = self.toggle_choice
+ self.key_func['^M'] = self.engage
+ self.resize()
+ self.update_commands()
+ self.update_keys()
+
+ def resize(self):
+ self.need_resize = False
+ self.topic_win.resize(1, self.width, 0, 0)
+ self.qr_win.resize(self.height-3, self.width, 1, 0)
+ self.help_win.resize(1, self.width, self.height-1, 0)
+
+ def refresh(self):
+ if self.need_resize:
+ self.resize()
+ log.debug(' TAB Refresh: %s', self.__class__.__name__)
+ self.refresh_tab_win()
+ self.info_win.refresh()
+ self.topic_win.refresh()
+ self.qr_win.refresh()
+ self.help_win.refresh()
+
+ def on_input(self, key, raw):
+ if not raw and key in self.key_func:
+ return self.key_func[key]()
+
+ def toggle_choice(self):
+ log.debug(' TAB toggle_choice: %s', self.__class__.__name__)
+ self.qr_win.toggle_choice()
+ self.refresh()
+ self.core.doupdate()
+
+ def engage(self):
+ log.debug(' TAB engage: %s', self.__class__.__name__)
+ if self.qr_win.engage():
+ self.core.close_tab(self)
+ else:
+ self.refresh()
+ self.core.doupdate()
+
+class Plugin(BasePlugin):
+ def init(self):
+ self.api.add_command(
+ 'qr',
+ self.command_qr,
+ usage='<message>',
+ short='Display a QR code',
+ help='Display a QR code of <message> in a new tab')
+ self.api.add_command(
+ 'invitation',
+ self.command_invite,
+ usage='[<server>]',
+ short='Invite a user',
+ help='Generate a XEP-0401 invitation on your server or on <server> and display a QR code')
+
+ def command_qr(self, msg):
+ t = QrTab(self.core, msg)
+ self.core.add_tab(t, True)
+ self.core.doupdate()
+
+ def on_next(self, iq, adhoc_session):
+ status = iq['command']['status']
+ xform = iq.xml.find(
+ '{http://jabber.org/protocol/commands}command/{jabber:x:data}x')
+ if xform is not None:
+ form = self.core.xmpp.plugin['xep_0004'].build_form(xform)
+ else:
+ form = None
+ uri = None
+ if status == 'completed' and form:
+ for field in form:
+ log.debug(' field: %s -> %s', field['var'], field['value'])
+ if field['var'] == 'landing-url' and field['value']:
+ uri = field.get_value(convert=False)
+ if field['var'] == 'uri' and field['value'] and uri is None:
+ uri = field.get_value(convert=False)
+ if uri:
+ t = QrTab(self.core, uri)
+ self.core.add_tab(t, True)
+ self.core.doupdate()
+ else:
+ self.core.handler.next_adhoc_step(iq, adhoc_session)
+
+
+ @command_args_parser.quoted(0, 1, defaults=[])
+ def command_invite(self, args):
+ server = self.core.xmpp.boundjid.domain
+ if len(args) > 0:
+ server = safeJID(args[0])
+ session = {
+ 'next' : self.on_next,
+ 'error': self.core.handler.adhoc_error
+ }
+ self.core.xmpp.plugin['xep_0050'].start_command(server, 'urn:xmpp:invite#invite', session)
+
diff --git a/plugins/reorder.py b/plugins/reorder.py
index 8d9516f8..7be0b350 100644
--- a/plugins/reorder.py
+++ b/plugins/reorder.py
@@ -59,6 +59,8 @@ And finally, the ``[tab name]`` must be:
- For a type ``static``, the full JID of the contact
"""
+from slixmpp import InvalidJID, JID
+
from poezio import tabs
from poezio.decorators import command_args_parser
from poezio.plugin import BasePlugin
@@ -162,21 +164,32 @@ class Plugin(BasePlugin):
new_tabs += [
tabs.GapTab(self.core) for i in range(pos - last - 1)
]
- cls, name = tabs_spec[pos]
- tab = self.core.tabs.by_name_and_class(name, cls=cls)
- if tab and tab in old_tabs:
- new_tabs.append(tab)
- old_tabs.remove(tab)
- else:
- self.api.information('Tab %s not found' % name, 'Warning')
+ cls, jid = tabs_spec[pos]
+ try:
+ jid = JID(jid)
+ tab = self.core.tabs.by_name_and_class(str(jid), cls=cls)
+ if tab and tab in old_tabs:
+ new_tabs.append(tab)
+ old_tabs.remove(tab)
+ else:
+ self.api.information('Tab %s not found. Creating it' % jid, 'Warning')
+ # TODO: Add support for MucTab. Requires nickname.
+ if cls in (tabs.DynamicConversationTab, tabs.StaticConversationTab):
+ new_tab = cls(self.core, jid)
+ new_tabs.append(new_tab)
+ except:
+ self.api.information('Failed to create tab \'%s\'.' % jid, 'Error')
if create_gaps:
new_tabs.append(tabs.GapTab(self.core))
- last = pos
+ finally:
+ last = pos
for tab in old_tabs:
if tab:
new_tabs.append(tab)
+ # TODO: Ensure we don't break poezio and call this with whatever
+ # tablist we have. The roster tab at least needs to be in there.
self.core.tabs.replace_tabs(new_tabs)
self.core.refresh_window()
diff --git a/plugins/upload.py b/plugins/upload.py
index 7e25070e..5e6dfb04 100644
--- a/plugins/upload.py
+++ b/plugins/upload.py
@@ -16,6 +16,9 @@ This plugin adds a command to the chat tabs.
"""
+
+from typing import Optional
+
import asyncio
import traceback
from os.path import expanduser
@@ -30,7 +33,11 @@ from poezio import tabs
class Plugin(BasePlugin):
+ dependencies = {'embed'}
+
def init(self):
+ self.embed = self.refs['embed']
+
if not self.core.xmpp['xep_0363']:
raise Exception('slixmpp XEP-0363 plugin failed to load')
for _class in (tabs.PrivateTab, tabs.StaticConversationTab, tabs.DynamicConversationTab, tabs.MucTab):
@@ -43,18 +50,23 @@ class Plugin(BasePlugin):
short='Upload a file',
completion=self.completion_filename)
- async def async_upload(self, filename):
+ async def upload(self, filename) -> Optional[str]:
try:
url = await self.core.xmpp['xep_0363'].upload_file(filename)
except UploadServiceNotFound:
self.api.information('HTTP Upload service not found.', 'Error')
- return
+ return None
except Exception:
exception = traceback.format_exc()
self.api.information('Failed to upload file: %s' % exception,
'Error')
- return
- self.core.insert_input_text(url)
+ return None
+ return url
+
+ async def send_upload(self, filename):
+ url = await self.upload(filename)
+ if url is not None:
+ self.embed.embed_image_url(url)
@command_args_parser.quoted(1)
def command_upload(self, args):
@@ -63,7 +75,7 @@ class Plugin(BasePlugin):
return
filename, = args
filename = expanduser(filename)
- asyncio.ensure_future(self.async_upload(filename))
+ asyncio.ensure_future(self.send_upload(filename))
@staticmethod
def completion_filename(the_input):