# OpenDHT The documentation related to the API of OpenDHT is [here](https://github.com/savoirfairelinux/opendht/wiki/API-Overview) and will not be detailed in the following part. # Daemon ## The managers The API of the daemon is decomposed between 5 Managers + 1 Instance file: + The **CallManager** interface is used to manage call and conference related actions. Since Ring-daemon supports multiple incoming/outgoing calls, any actions involving a specific call must address the method by the means of a unique callID. Ring-daemon will generate a unique callID for outgoing and incoming calls. + The **ConfigurationManager** used to handle the configuration stuff: accounts settings, user preferences, ... + The **PresenceManager** is used to track the presence of contacts + The **VideoManager** used to manage video devices and renderers + The **Instance** is used to count the number of clients actually registered to the core. When initializing your client, you need to register it against the core by using this interface. ## DBUS All the documentation and code for the dbus API is located in `ring-daemon/bin/dbus`. If you use linux, you can use `d-feet` when the daemon is running to manipulate the API (or with any another tool). The LRC project uses this API (and use libwrap on windows and mac os). ## JNI All the documentation and code for the JNI API is located in `ring-daemon/bin/jni`. ### Generation process - `cd ring-project/` - Check `./daemon/src/dring` - Create a new file .i in `./daemon/bin/jni/` from an existing file in the same folder - Update with your new interfaces from documentation - Check that callback block is at the beginning AND at the end of file - `cd daemon/bin/jni` - Edit ./daemon/bin/jni/jni\_interface.i and add yours with the other .i files that are in the same folder - `export PACKAGEDIR=\*\*\*\*/ring-project/client-android/ring-android/libringclient/src/main/java/cx/ring/daemon.` ie: `export PACKAGEDIR=/home/pduchemin/Documents/ring-project/client-android/ring-android/libringclient/src/main/java/cx/ring/daemon` - Run - `./make-swig.sh` - or, if you start from scratch - `./make-ring.py --init --distribution=Android` - `./make-ring.py --install --distribution=Android` ## node js All the documentation and code for the Node JS API is located in `ring-daemon/bin/nodejs`. This API is not used in any known project and maybe is not up-to-date. ## REST All the documentation and code for the REST API is located in `ring-daemon/bin/restcpp`. This API is not used in any known project and maybe is not up-to-date. ## Python wrapper A Python wrapper is available in `ring-daemon/tools/dringctrl`. This wrapper uses d-bus. This is, for example a quick and dirty IRC bot to interact with Ring made with this API: `config.json`: ```json { "users": { }, "debug": true, "host": "irc.freenode.net", "follow_all": { }, "port": 6697, "nick": "NICKNAME", "ssl": true, "channel": "#REPLACE" } ``` `irc,py`: ```python #!/usr/bin/env python3 # -*- coding: utf-8 -*- from controler import DRingCtrl from threading import Thread import irc3 from irc3 import utils import json import sys @irc3.plugin class RingBridge: '''This plug-in react to event from IRC and send messages to IRC''' requires = [ 'irc3.plugins.core', 'irc3.plugins.userlist', 'irc3.plugins.command', 'irc3.plugins.human', ] def __init__(self, bot): self.bot = bot self.log = self.bot.log self.channels = utils.as_list(self.bot.config.get('autojoins', [])) self.config = None @irc3.event(irc3.rfc.PRIVMSG) def on_privmsg(self, mask=None, target=None, data=None, **kwargs): '''React to messages from IRC''' message = data.split(' ') follow_all = self.bot.config_bridge['follow_all'] if message[0] == '!tell': msg_to_send = ('%s: ' % mask.nick) + ''.join( '%s ' % m for m in message[2:])[:-1] call_id = None users = self.bot.config_bridge['users'] # Get ring_id from nick for ring_id in users.keys(): if users[ring_id].lower() == message[1].lower(): call_id = ring_id if call_id: # if this nick want to receive all messages, we don't need # to send the message here if call_id not in follow_all.keys() \ or not follow_all[call_id] \ or target == self.bot.config_bridge['channel']: print('Send: %s to %s' % (msg_to_send, call_id)) self.bot.controler.sendTextMessage(call_id, msg_to_send) else: self.bot.privmsg(self.bot.config_bridge['channel'], 'I don\'t know how to contact this person') # Send message to everyone who wants to receive message from #channel if target.lower() != self.bot.config_bridge['channel'].lower(): return msg_to_send = ('%s: %s' % (mask.nick, data)) for user in follow_all.keys(): if follow_all[user]: self.bot.controler.sendTextMessage(user, msg_to_send) @irc3.extend def send_message_to_irc(self, ring_id, message): '''send message to #channel''' self.bot.privmsg(self.bot.config_bridge['channel'], '%s: %s' % (self.bot.config_bridge['users'][ring_id], message), True) def threadClient(controler, config_bridge): config = dict( nick=config_bridge['nick'], autojoins=[config_bridge['channel']], host=config_bridge['host'], port=config_bridge['port'], ssl=config_bridge['ssl'], debug=config_bridge['debug'], includes=[__name__] ) bot = irc3.IrcBot.from_config(config) # link irc and ring controlers bot.controler = controler bot.config_bridge = config_bridge controler.irc_client = bot bot.run(forever=True) class IRCRingController(DRingCtrl): def __init__(self, config_bridge): super().__init__('ircbridge', True) self.irc_client = None self.config_bridge = config_bridge self.accountId = self.configurationmanager.getAccountList()[0] def updateConfig(self): '''Change actual config''' with open('config.json', 'w') as config_file: json.dump(self.config_bridge, config_file, indent=4) if self.irc_client: self.irc_client.config_bridge = self.config_bridge def onIncomingAccountMessage(self, accountId, fromAccount, payloads): '''React to message from Ring''' # Avoid to react to message from self. if fromAccount == self.accountId: return # If we have multiple accounts, avoid to react from another account if accountId != self.accountId: return message = '%s: %s' % (fromAccount, payloads['text/plain']) print('Receive new message from %s' % message) # React to !commands if payloads['text/plain'] == '!follow': self.config_bridge['follow_all'][fromAccount] = True self.updateConfig() elif payloads['text/plain'] == '!unfollow': self.config_bridge['follow_all'][fromAccount] = False self.updateConfig() elif payloads['text/plain'].split(' ')[0] == '!add': irc_nick = payloads['text/plain'].split(' ')[1] self.config_bridge['users'][fromAccount] = irc_nick self.updateConfig() elif payloads['text/plain'] == '!rm': del self.config_bridge['users'][fromAccount] del self.config_bridge['follow_all'][fromAccount] self.updateConfig() # Send message to IRC else: try: if self.irc_client: self.irc_client.send_message_to_irc(fromAccount, payloads['text/plain']) except: print('Can\'t read message received: %s' % payloads) def sendTextMessage(self, accountId, message): '''Send a message to a ring id''' if accountId == self.accountId: return self.configurationmanager.sendTextMessage(self.accountId, accountId, { 'text/plain': str(message) }) if __name__ == '__main__': config_bridge = None with open('config.json') as config_file: config_bridge = json.loads(config_file.read()) if not config_bridge: print('Can\'t find config.json') sys.exit(-1) irc_controler = IRCRingController(config_bridge) thread = Thread(target=threadClient, args=(irc_controler, config_bridge,)) thread.start() irc_controler.run() ``` # LRC ## Doxygen doc The Doxygen documentation is available [here](https://jenkins.ring.cx/view/libringclient/job/libringclient-doxygen/doxygen/annotated.html) and currently generated by Jenkins each week. ## Database schema ![lrcbd](/uploads/94bee0c65b2a87b0f3e1ee223ccf81dc/lrcbd.jpg) ```sql CREATE TABLE profiles (id INTEGER PRIMARY KEY, \ uri TEXT NOT NULL, \ alias TEXT, \ photo TEXT, \ type TEXT, \ status TEXT); CREATE TABLE conversations (id INTEGER,\ participant_id INTEGER, \ FOREIGN KEY(participant_id) REFERENCES profiles(id)); CREATE TABLE interactions (id INTEGER PRIMARY KEY,\ account_id INTEGER, \ author_id INTEGER, \ conversation_id INTEGER, \ timestamp INTEGER, \ body TEXT, \ type TEXT, \ status TEXT, \ daemon_id TEXT, \ FOREIGN KEY(account_id) REFERENCES profiles(id), \ FOREIGN KEY(author_id) REFERENCES profiles(id), \ FOREIGN KEY(conversation_id) REFERENCES conversations(id)); CREATE TABLE profiles_accounts (profile_id INTEGER NOT NULL, \ account_id TEXT NOT NULL, \ is_account TEXT, \ FOREIGN KEY(profile_id) REFERENCES profiles(id)); ```