mirror of
https://git.jami.net/savoirfairelinux/jami-docs.git
synced 2025-07-03 06:07:56 +08:00
279 lines
11 KiB
Markdown
279 lines
11 KiB
Markdown
# 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
|
|
|
|

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