Skip to content

Instantly share code, notes, and snippets.

@prauscher
Last active December 7, 2016 13:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save prauscher/0365149e60dc697d1e6eacb9cc49bff6 to your computer and use it in GitHub Desktop.
Save prauscher/0365149e60dc697d1e6eacb9cc49bff6 to your computer and use it in GitHub Desktop.
Script to automatically call and connect you to the conference room a minute before your next phone conference starts according to your calendar
# Of course this is just the (relevant) part of my extensions.ael. Modify this to your needs and add it to the bottom of yours
context connect_remote {
loc => {
Dial(${SOURCE});
}
rmt => {
Dial(${DESTINATION},,${CALL_OPTIONS});
}
}
#!/usr/bin/env python3
from icalendar import Calendar
import requests
from datetime import timedelta as tdelta
from datetime import datetime as dt
from dateutil import rrule as rrule
import re
import hashlib
import os, stat
# Data sources (URL to your ICAL-Files, e.g. Google Calendar export)
calendars = []
# Which Location we are able to parse and which asterisk-channels they are
location_patterns = [
("(0[0-9]+)", "SIP/sipgate/\\1"),
("(\+[0-9]+)", "SIP/sipgate/\\1"),
("tel:(\+?[0-9]+)", "SIP/sipgate/\\1"),
("sip:([^\s]+@[^\s]+)", "SIP/\\1"),
]
# How many minutes before the event starts should the call initiated?
graceTime = 1
# How often this script gets called (how long it should check in the future)
resolution = 60 * 60
# Pattern for the generated callfiles. May use Parameters #0: Asterisk-Channel to dial (as defined in location_patterns), #1: DTMF-tones to send, #2: Summary of the event
# Note that you can define the numbers to call using SOURCE (currently +4912345678)
callfilePattern = """
Channel: Local/loc@connect_remote
CallerID: "{2}" <0>
Context: connect_remote
Extension: rmt
Setvar: CALL_OPTIONS=D(ww{1})
Setvar: SOURCE=SIP/sipgate/+4912345678
Setvar: DESTINATION={0}
"""
def create_call(uid, summary, destination, room, dtstart):
filename = "{}.call".format(hashlib.sha1(uid).hexdigest())
timestamp = dtstart.timestamp() - 60 * graceTime
callfile = open("/tmp/call_" + filename, "w")
callfile.write(callfilePattern.format(destination, room, summary))
callfile.close()
os.utime("/tmp/call_" + filename, (timestamp, timestamp))
os.chmod("/tmp/call_" + filename, stat.S_IRWXU + stat.S_IRWXG + stat.S_IRWXO)
os.rename("/tmp/call_" + filename, "/var/spool/asterisk/outgoing/" + filename)
match_locations = [(re.compile(pattern), replacement) for pattern, replacement in location_patterns]
class MatchingRRuleException(Exception):
pass
class NoDateTimeException(Exception):
pass
class NoPhoneNumberException(Exception):
pass
def handle_calendar(ical_text):
cal = Calendar.from_ical(ical_text)
for event in cal.walk("VEVENT"):
try:
uid = event.decoded("uid")
summary = event.decoded("summary").decode("utf-8")
location = event.decoded("location").decode("utf-8").split(" ")[0:2]
dtstart = event.decoded("dtstart")
# Filter out full-day-events
if not isinstance(dtstart, dt):
raise NoDateTimeException()
# need to compare two values with timezone-data, so use the same timezone as in iCal
now = dt.now(dtstart.tzinfo)
# for recurring events, use the next instance
if "rrule" in event:
rr_ical = event.decoded("rrule").to_ical().decode("utf-8")
rr = rrule.rrulestr(rr_ical, dtstart=dtstart)
dtstart = rr.after(now)
# Filter events where last recurrence has already happened
if dtstart is None:
raise MatchingRRuleException()
# search for the destination to use
destination = None
for expression, replacement in match_locations:
match = expression.fullmatch(location[0])
if match:
destination = match.expand(replacement)
break
if destination is None:
raise NoPhoneNumberException()
# check if this event should be created now
if now < dtstart and now + tdelta(seconds=resolution) >= dtstart:
create_call(uid, summary, destination, location[1], dtstart)
except (MatchingRRuleException, NoDateTimeException, NoPhoneNumberException):
pass
for calendar_url in calendars:
handle_calendar(requests.get(calendar_url).text)
[Unit]
Description=Unit to check for new Telko-Events
[Service]
Type=oneshot
# Change this according to your needs
ExecStart=/usr/local/bin/telko-inviter.py
[Unit]
Description=Run telko-inviter.py update every 15 minutes
[Timer]
OnBootSec=10min
OnUnitInactiveSec=15min
[Install]
WantedBy=timers.target
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment