Last active
December 7, 2016 13:34
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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}); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[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