Commit dc8734f9 authored by Citronalco's avatar Citronalco
Browse files

update to python3

replace asterisk module with panoramisk
use ini file for settings
update README

thanks to tbhi! (see https://github.com/tbhi/carddav2asterisk)
parent 8d9d0249
# CardDAV-to-Asterisk import
**Note: tbhi has updated this script, including Python 3 support: https://github.com/tbhi/carddav2asterisk - I haven't tried it yet, though.**
This script reads all contacts from a CardDAV addressbook and puts them into Asterisk's internal caller id database.
This script reads all contacts from a CardDAV address book and puts them into Asterisk's internal caller id database.
That way Asterisk can show a caller's name instead of just the number.
If there's already an entry in Asterisk's caller database for a specific number, this script updates the number with the new name.
So far I have only tested with OwnCloud 9 and Nextcloud 13.
So far I have run this script successfully with OwnCloud 9 to Nextcloud 19.
### Requirements
* Python 2
* Additional Python 2 modules: requests, vobjects, pyst, lxml
(Debian packages: python-requests, python-vobject, python-pyst, python-lxml)
* Python 3
* Additional Python 3 modules: requests, vobjects, panoramisk, lxml
(Install in Debian/Ubuntu/Mint: `apt-get install python3-requests python3-vobject python3-panoramisk python3-lxml`)
### Usage
#### Configuration
1. This script uses Asterisk Manager to access the caller id database, so you need to set up an user with "system" permissions if you have not already done so. To add an user create the file "/etc/asterisk/manager.d/carddav2asterisk.conf":
```
[carddavimport]
secret = cidpwd
permit = 127.0.0.1/255.255.255.0
read = system
write = system
```
2. Open the script in an editor and edit the user's credentials and host and port of Asterisk's Manager interface:
```
# ASTERISK MANAGER CONNECTION
HOST = 'localhost'
PORT = 5038
USER = 'carddavimport'
PASS = 'cidpwd'
```
3. Change NATIONALPREFIX and DOMESTICPREFIX to match your location:
```
# PERSONAL SETTINGS
NATIONALPREFIX = "0049"
DOMESTICPREFIX = "0841"
```
4. If you haven't configured CID lookup in Asterisk yet, you may want to add something like this somewhere at the beginning of your dialplan in extensions.conf:
1. This script uses Asterisk Manager Interface (AMI) to write to the caller id database, so you need to set up an user with "write" permissions to "system":
To add such an user, create a file `/etc/asterisk/manager.d/carddav2asterisk.conf` like this:
[carddavimport]
secret = TopSecret
permit = 127.0.0.1/255.255.255.0
write = system
After creating the file reload Asterisk.
2. Create an ini file like this (you may use the included `example.ini` as a starting point):
[ami]
user = carddavimport
pass = TopSecret
host = localhost
port = 5038
[carddav]
url = https://nextcloud.example.com/remote.php/dav/addressbooks/users/russmeyer/contacts/
user = russmeyer
pass = Evelyn1928
[phone]
nationalprefix = 0049
domesticprefix = 089
The **[ami]** section must contain the connection parameters to Asterisk Manager Interface.
The **[carddav]** section must contain the connection parameters to your CardDAV server.
The **[phone]** section is optional. You may use this to clean up the phone numbers before they get written into Asterisk's database.
Set ***nationalprefix*** to your national prefix to _remove_ it from phone numbers.
Set ***domesticprefix*** to your local area code to _add_ it to phone numbers without domestic prefix.
If in doubt simply omit the **[phone]** section.
3. If you haven't configured CID lookup in Asterisk yet, you may want to add something like this somewhere at the beginning of your dialplan in `extensions.conf`:
``exten => <yourExtension>,n,Set(CALLERID(name)=${IF(${DB_EXISTS(cidname/${CALLERID(num)})}?${DB(cidname/${CALLERID(num)})}:${CALLERID(name)})})``
#### How to run the script
``./carddav2asterisk.py <URl to CardDAV addressbook> <CardDAV username> <CardDAV password>``
``./carddav2asterisk.py [--no-update] <ini file>``
**Example:**
``./carddav2asterisk.py example.ini``
If you use the `--no-update` switch, the script will not write anything to Asterisk but only show what would be done.
Example:
``./carddav2asterisk.py https://owncloud.example.com/remote.php/dav/addressbooks/users/russmeyer/contacts/ meyerr pa55w0rd``
#!/usr/bin/env python2
#!/usr/bin/env python3
#-*- coding: utf-8 -*-
import sys
import requests
import vobject
import re
from asterisk import manager
import asyncio
from panoramisk import Manager
from requests.auth import HTTPBasicAuth
from lxml import etree
from urlparse import urlparse
from urllib.parse import urlparse
import argparse
import configparser
# ASTERISK MANAGER CONNECTION
HOST = 'localhost'
PORT = 5038
USER = 'carddavimport'
PASS = 'cidpwd'
# PERSONAL SETTINGS
NATIONALPREFIX = "0049"
DOMESTICPREFIX = "0841"
# get list with links to all available vcards
def getAllVcardLinks(url,auth):
baseurl = urlparse(url).scheme+'://'+urlparse(url).netloc
r = requests.request('PROPFIND',url,auth=auth)
root = etree.XML(r.content)
vcardUrlList=[]
for record in root.xpath(".//d:response",namespaces={"d" : "DAV:"}):
type = record.xpath(".//d:getcontenttype",namespaces={"d" : "DAV:"})
def getAllVcardLinks(url, auth):
baseurl = urlparse(url).scheme+'://' + urlparse(url).netloc
r = requests.request('PROPFIND', url, auth = auth)
if r.status_code != 207:
raise RuntimeError('error in response from %s: %r' % (url, r))
root = etree.XML(r.text)
vcardUrlList = []
for record in root.xpath(".//d:response", namespaces = {"d" : "DAV:"}):
type = record.xpath(".//d:getcontenttype", namespaces = {"d" : "DAV:"})
if (type) and type[0].text.startswith("text/vcard"):
vcardlinks = record.xpath(".//d:href",namespaces={"d" : "DAV:"})
vcardlinks = record.xpath(".//d:href", namespaces = {"d" : "DAV:"})
for link in vcardlinks:
vcardUrlList.append(baseurl + '/' + link.text);
vcardUrlList.append(baseurl + link.text)
return vcardUrlList
def tidyPhoneNumber(num):
num = re.sub("^\+","00",num) # +39 -> 0039
num = re.sub("\D","",num) # remove all non-digits
num = re.sub("^"+NATIONALPREFIX+"0*","0",num) # strip own national prefix
num = re.sub("^[^0]","0"+DOMESTICPREFIX,num) # add domestic prefix, if missing
def tidyPhoneNumber(config, num):
num = re.sub("^\+", "00", num) # +39 -> 0039
num = re.sub("\D", "", num) # remove all non-digits
if 'phone' in config:
if 'nationalprefix' in config['phone']:
num = re.sub("^" + config['phone']['nationalprefix'] + "0*", "0", num) # strip own national prefix
if 'domesticprefix' in config['phone']:
num = re.sub("^[^0]", "0" + config['phone']['domesticprefix'], num) # add domestic prefix, if missing
return num
def main(argv):
auth = HTTPBasicAuth(argv[2],argv[3])
url = argv[1]
def main():
parser = argparse.ArgumentParser()
parser.add_argument('ini_file')
parser.add_argument("--no-update", help="Don't write to asterisk", action='store_true', default=False)
args = parser.parse_args()
config = configparser.RawConfigParser()
config.read(args.ini_file)
loop = asyncio.get_event_loop()
loop.run_until_complete(putCids(loop, args, config))
loop.close()
def putCids(lp, args, config):
auth = HTTPBasicAuth(config['carddav']['user'], config['carddav']['pass'])
url = config['carddav']['url']
# connect to asterisk
ami = manager.Manager()
ami.connect(HOST)
ami.login(USER,PASS)
ami = Manager(host = config['ami']['host'],
port = config['ami']['port'],
username = config['ami']['user'],
secret = config['ami']['pass'])
yield from ami.connect()
# get phone numbers from vcard
for vurl in getAllVcardLinks(url,auth):
r = requests.request("GET",vurl,auth=auth)
vcard = vobject.readOne(r.content)
for vurl in getAllVcardLinks(url, auth):
r = requests.request("GET", vurl, auth=auth)
vcard = vobject.readOne(r.text)
if "tel" in vcard.contents:
for telno in vcard.contents['tel']:
num = tidyPhoneNumber(telno.value)
num = tidyPhoneNumber(config, telno.value)
if "fn" in vcard.contents:
name = vcard.fn.value
print("Adding/updating Number: "+num+" Name: "+name)
ami.send_action({"Action": "DBPut", "Family": "cidname", "Key": num, "Val": name})
ami.logoff()
print("Adding/updating Number: %s Name: %s" % (num, name), end="... ")
if not args.no_update:
ami_result = yield from ami.send_action({"Action": "DBPut", "Family": "cidname", "Key": num, "Val": name})
print(ami_result.Response)
else:
print("no-update")
ami.close()
if __name__ == "__main__":
if len(sys.argv)!=4:
print("Must be called with three arguments: <carddav-url> <carddav-user> <carddav-password>")
print("Example: %s https://owncloud.example.com/remote.php/dav/addressbooks/users/russmeyer/contacts/ meyerr p8a55w0rd" % sys.argv[0])
sys.exit(1)
sys.exit(main(sys.argv))
main()
[ami]
user = carddavimport
pass = TopSecret
host = localhost
port = 5038
[carddav]
url = https://nextcloud.example.com/remote.php/dav/addressbooks/users/russmeyer/contacts/
user = russmeyer
pass = Evelyn1928
#[phone]
#nationalprefix = 0049
#domesticprefix = 089
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment