2018-11-14 09:31:42 +08:00
# 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
2018-11-15 12:23:09 +08:00
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.
2018-11-14 09:31:42 +08:00
## DBUS
2018-11-15 12:23:09 +08:00
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).
2018-11-14 09:31:42 +08:00
## JNI
2018-11-15 12:23:09 +08:00
All the documentation and code for the JNI API is located in `ring-daemon/bin/jni` .
2018-11-14 09:31:42 +08:00
2018-11-15 12:23:09 +08:00
### 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`
2018-11-14 09:31:42 +08:00
2018-11-15 12:23:09 +08:00
## 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
2018-11-14 09:31:42 +08:00
2018-11-15 12:25:46 +08:00
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()
2018-11-26 06:50:10 +08:00
```
# LRC
## Doxygen doc
2018-11-26 07:08:57 +08:00
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
2018-11-26 07:11:17 +08:00

```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));
```