[f3af614] | 1 | #!/usr/bin/python |
---|
| 2 | |
---|
| 3 | import bjsonrpc |
---|
| 4 | from bjsonrpc.handlers import BaseHandler |
---|
| 5 | |
---|
| 6 | import re |
---|
| 7 | import socket |
---|
[2533d10] | 8 | |
---|
[f3af614] | 9 | # List of functions an IM plugin can export. This library will indicate to |
---|
| 10 | # BitlBee which functions are actually implemented so omitted features |
---|
| 11 | # will be disabled, but note that some/many functions are simply mandatory. |
---|
[f8feb8a] | 12 | # (Currently login/-out, buddy_msg.) |
---|
[f3af614] | 13 | SUPPORTED_FUNCTIONS = [ |
---|
| 14 | 'login', 'keepalive', 'logout', 'buddy_msg', 'set_away', |
---|
| 15 | 'send_typing', 'add_buddy', 'remove_buddy', 'add_permit', |
---|
| 16 | 'add_deny', 'rem_permit', 'rem_deny', 'get_info', 'chat_invite', |
---|
| 17 | 'chat_kick', 'chat_leave', 'chat_msg', 'chat_with', 'chat_join', |
---|
| 18 | 'chat_topic' |
---|
| 19 | ] |
---|
| 20 | |
---|
[b20014b] | 21 | def make_version_tuple(hex): |
---|
| 22 | """Convert the BitlBee binary-encoded version number into something |
---|
| 23 | more "Pythonic". Could use distutils.version instead but its main |
---|
| 24 | benefit appears to be string parsing which here is not that useful.""" |
---|
| 25 | |
---|
| 26 | return (hex >> 16, (hex >> 8) & 0xff, hex & 0xff) |
---|
| 27 | |
---|
[f3af614] | 28 | class RpcForwarder(object): |
---|
| 29 | """Tiny object that forwards RPCs from local Python code to BitlBee |
---|
| 30 | with a marginally nicer syntax. This layer could eventually be |
---|
| 31 | used to add basic parameter checking though I don't think that should |
---|
| 32 | be done here.""" |
---|
| 33 | |
---|
| 34 | def __init__(self, methods, target): |
---|
| 35 | for m in methods: |
---|
| 36 | # imc(b)_ prefix is not useful here, chop it. |
---|
| 37 | # (Maybe do this in BitlBee already as well.) |
---|
| 38 | newname = re.sub("^imcb?_", "", m) |
---|
| 39 | self.__setattr__(newname, target.__getattr__(m)) |
---|
| 40 | |
---|
| 41 | class BitlBeeIMPlugin(BaseHandler): |
---|
[c5a7b8d] | 42 | # Protocol name to be used in the BitlBee CLI, etc. |
---|
[f3af614] | 43 | NAME = "rpc-test" |
---|
[c5a7b8d] | 44 | |
---|
| 45 | # See account.h (TODO: Add constants.) |
---|
[e9face7] | 46 | ACCOUNT_FLAGS = 0 |
---|
[c5a7b8d] | 47 | |
---|
| 48 | # Supported away states. If your protocol supports a specific set of |
---|
| 49 | # away states, put them in a list in this variable. |
---|
[2533d10] | 50 | AWAY_STATES = None |
---|
[600e09c] | 51 | |
---|
| 52 | SETTINGS = { |
---|
| 53 | "oauth": { |
---|
| 54 | "default": False, |
---|
| 55 | "type": "bool", |
---|
| 56 | }, |
---|
| 57 | "test": { |
---|
| 58 | "default": 123, |
---|
| 59 | "type": "int", |
---|
| 60 | }, |
---|
| 61 | "stringetje": { |
---|
| 62 | "default": "testje", |
---|
| 63 | "flags": 0x04, |
---|
| 64 | } |
---|
| 65 | } |
---|
| 66 | _settings_values = {} |
---|
[f3af614] | 67 | |
---|
| 68 | # Filled in during initialisation: |
---|
[b20014b] | 69 | # Version number as a three-tuple, so 3.2 becomes (3, 2, 0). |
---|
[f3af614] | 70 | bitlbee_version = None |
---|
| 71 | # Full version string |
---|
| 72 | bitlbee_version_str = None |
---|
[c5a7b8d] | 73 | # Will become an RpcForwarder object to call into BitlBee |
---|
| 74 | bee = None |
---|
[f3af614] | 75 | |
---|
| 76 | @classmethod |
---|
| 77 | def _factory(cls, *args, **kwargs): |
---|
| 78 | def handler_factory(connection): |
---|
| 79 | handler = cls(connection, *args, **kwargs) |
---|
| 80 | return handler |
---|
| 81 | return handler_factory |
---|
| 82 | |
---|
| 83 | def init(self, bee): |
---|
| 84 | self.bee = RpcForwarder(bee["method_list"], self._conn.call) |
---|
[b20014b] | 85 | self.bitlbee_version = make_version_tuple(bee["version"]) |
---|
[f3af614] | 86 | self.bitlbee_version_str = bee["version_str"] |
---|
[2533d10] | 87 | |
---|
[f3af614] | 88 | return { |
---|
| 89 | "name": self.NAME, |
---|
| 90 | "method_list": list(set(dir(self)) & set(SUPPORTED_FUNCTIONS)), |
---|
[c5a7b8d] | 91 | "account_flags": self.ACCOUNT_FLAGS, |
---|
| 92 | "away_state_list": self.AWAY_STATES, |
---|
[600e09c] | 93 | "settings": self.SETTINGS, |
---|
[f3af614] | 94 | } |
---|
| 95 | |
---|
[600e09c] | 96 | def login(self, account): |
---|
| 97 | for key, value in account.get("settings", {}).iteritems(): |
---|
| 98 | self.set_set(key, value) |
---|
| 99 | |
---|
| 100 | def set_set(self, key, value): |
---|
| 101 | self._settings_values[key] = value |
---|
| 102 | try: |
---|
| 103 | func = self.__getattribute__("set_set_%s" % key) |
---|
| 104 | except AttributeError: |
---|
[e9face7] | 105 | return |
---|
[600e09c] | 106 | func(key, value) |
---|
| 107 | |
---|
| 108 | def setting(self, key): |
---|
[b09ce17] | 109 | """Throws KeyError if the setting does not exist!""" |
---|
[600e09c] | 110 | return self._settings_values[key] |
---|
[e9face7] | 111 | |
---|
[f3af614] | 112 | |
---|
[600e09c] | 113 | def RunPlugin(plugin, debug=False): |
---|
[f3af614] | 114 | sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) |
---|
| 115 | sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) |
---|
[600e09c] | 116 | sock.bind("/tmp/rpcplugins/%s.sock" % plugin.NAME) |
---|
[f3af614] | 117 | sock.listen(3) |
---|
| 118 | |
---|
| 119 | srv = bjsonrpc.server.Server(sock, plugin._factory()) |
---|
| 120 | |
---|
| 121 | srv.debug_socket(debug) |
---|
| 122 | srv.serve() |
---|