Skip to content
This repository has been archived by the owner on Dec 22, 2023. It is now read-only.

Commit

Permalink
Merge pull request #190 from Bytespeicher/master
Browse files Browse the repository at this point in the history
Update branch content for pr
  • Loading branch information
jurkov authored Oct 26, 2017
2 parents efaee6d + 01435fb commit 89e0013
Show file tree
Hide file tree
Showing 8 changed files with 336 additions and 97 deletions.
19 changes: 14 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,26 @@
## Setup

* Fork / pull the project
git clone; git checkout $BRANCH

`git clone; git checkout $BRANCH`
* Create virtual environment
pyvenv env

```
pyvenv env`
. env/bin/activate
```
* Install dependencies (see [README](README.md))
pip install -r contrib/requirements.txt
* Setup a `config.ini`

`pip install -r contrib/requirements.txt`
* Setup a `config.ini

```
cp config.ini{.example,}
$EDITOR config.ini
```
* Start
irc3 config.ini

`irc3 config.ini`


## Coding Style
Expand Down
12 changes: 11 additions & 1 deletion config.ini.example
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,13 @@ url = https://fahrplan.events.ccc.de/congress/YEAR/Fahrplan/schedule.json

[plugins.dates]
url = http://www.google.com/calendar/ical/2eskb61g20prl65k2qd01uktis%40group.calendar.google.com/public/basic.ics
# timedelta = 21
cache = /tmp/dates.cache
# Days in the future to show on list request
list_days = 21
# Minutes in the future to show event announcement (space separated list for multiple values)
announce_minutes = 60 1440
# Do not show default location
filter_location = Bytespeicher

[plugins.fuel]
apikey = your_apikey
Expand Down Expand Up @@ -126,6 +132,10 @@ SpacestatusCommits =
clarifai_app_id = yourappid
clarifai_app_secret = yourappsecret

[plugins.station]
city = Erfurt
id = 151213

[plugins.weather]
api_key = your_apikey
url = http://api.openweathermap.org/data/2.5/weather?units=metric&q=
Expand Down
1 change: 1 addition & 0 deletions data/stations.latest.json

Large diffs are not rendered by default.

11 changes: 9 additions & 2 deletions plugins/cccongress.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def cccongress(bot, mask, target, args):


@cron('1 * 1-26,31 12 *')
@cron('*/15 * 27-30 12 *')
@cron('2-59/15 * 27-30 12 *')
@asyncio.coroutine
def cccongress_update_cron(bot):
"""Update schedule in December"""
Expand All @@ -70,7 +70,14 @@ def cccongress_announce_next_talks(bot):
designated_start = \
datetime.datetime.now(pytz.timezone('Europe/Berlin')) + \
datetime.timedelta(minutes=config['announce_minutes'])
if event_start == designated_start:
"""
Compare without seconds to prevent problems if
process runs multiple seconds
"""
designated_start_compare = \
designated_start.strftime('%Y-%m-%d %H:%M')
event_start_compare = event_start.strftime('%Y-%m-%d %H:%M')
if event_start_compare == designated_start_compare:
"""Save hall information in event and add to announcelist"""
event['hall'] = hall
announcelist.append(event)
Expand Down
175 changes: 145 additions & 30 deletions plugins/dates.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from irc3 import asyncio
from irc3.plugins.command import command
from irc3.plugins.cron import cron

import aiohttp
import time
Expand All @@ -8,10 +9,17 @@
from dateutil.rrule import rruleset, rrulestr
from dateutil.parser import parse
from icalendar import Calendar
from icalendar.prop import vDDDTypes
from icalendar.prop import vDDDTypes, vDDDLists
from pytz import utc, timezone


def dates_configuration(bot):
"""Load configuration"""
config = {'url': '', 'list_days': 21, 'announce_minutes': ''}
config.update(bot.config.get(__name__, {}))
return config


@command(permission="view")
@asyncio.coroutine
def dates(bot, mask, target, args):
Expand All @@ -20,27 +28,66 @@ def dates(bot, mask, target, args):
%%dates
"""

"""Load configuration"""
config = {'url': '', 'timedelta': 21}
config.update(bot.config.get(__name__, {}))
config = dates_configuration(bot)

now = datetime.utcnow().replace(hour=0,
minute=0,
second=0,
microsecond=0)

yield from _update_cache(bot)
yield from output_dates(bot,
target,
now,
now + timedelta(days=config['list_days']),
config['filter_location'])


@cron('*/15 * * * *')
@asyncio.coroutine
def cccongress_update_cron(bot):
"""Update ical file"""

yield from _update_cache(bot)

"""Request the ical file."""
with aiohttp.Timeout(10):
with aiohttp.ClientSession(loop=bot.loop) as session:
resp = yield from session.get(config['url'])
if resp.status == 200:
"""Get text content from http request."""
r = yield from resp.text()
else:
bot.privmsg(target, "Error while retrieving calendar data")
raise Exception()

@cron('* * * * *')
@asyncio.coroutine
def dates_announce_next_talks(bot):
"""Announce next dates"""

config = dates_configuration(bot)

now = datetime.utcnow().replace(second=0,
microsecond=0)

for minutes in config['announce_minutes'].split(' '):
yield from output_dates(bot,
bot.config.autojoins[0],
now + timedelta(minutes=int(minutes)),
now + timedelta(minutes=int(minutes)),
config['filter_location'],
int(minutes))


@asyncio.coroutine
def output_dates(bot, target, now, then, filter_location, announce=0):
"""
Output dates between now and then and filter default location.
Set announce greater 0 to add announce message and
suppresses the No dates found message.
"""

config = dates_configuration(bot)

try:
file = open(config['cache'])
r = file.read()
except OSError as e:
raise Exception(e)

try:
cal = Calendar.from_ical(r)
now = datetime.now().replace(
hour=0, minute=0, second=0, microsecond=0)
then = now + timedelta(
days=config['timedelta'])
found = 0

data = []
Expand Down Expand Up @@ -72,7 +119,6 @@ def dates(bot, mask, target, args):
discard handling the VEVENT here
"""
if "SUMMARY" in ev:
found += 1
info = ev["SUMMARY"]
else:
continue # events ohne summary zeigen wir nicht an!
Expand All @@ -81,14 +127,16 @@ def dates(bot, mask, target, args):
Printing the location of an event is important too.
However,
the string containing location info may be too long
to be viewed nicely in IRC. Since there exits no
good solution for this, this feature is coming soon.
to be viewed nicely in IRC.
We filter our default location and strip every other
location to the location name without address.
"""

if "LOCATION" in ev:
loc = ev["LOCATION"]
else:
loc = "Liebknechtstrasse 8"
if not ev["LOCATION"].startswith(filter_location):
loc = ev["LOCATION"].split(', ')[0]

"""
Recurrence handling starts here.
First, we check if there is a recurrence rule (RRULE)
inside the VEVENT, If so, we use the ical like
Expand All @@ -102,6 +150,20 @@ def dates(bot, mask, target, args):
dtstart=parse(ical_dtstart),
ignoretz=1))

"""
Recurrence handling includes exceptions in EXDATE.
First we check if there are EXDATE values. If there
is only one we will convert this also to a list to
simplify handling. We use list entries to feed our
ruleset with.
"""
if "EXDATE" in ev:
ical_exdate = ev.get('EXDATE')
if isinstance(ical_exdate, vDDDLists):
ical_exdate = [ical_exdate]
for exdate in ical_exdate:
rset.exdate(parse(exdate.to_ical()))

"""
the ruleset now may be used to calculate any datetime
the event happened and will happen.
Expand All @@ -115,6 +177,7 @@ def dates(bot, mask, target, args):
into our "database" of events
"""
for e in rset.between(now, then):
found += 1
data.append({
'datetime': e.strftime(fmt),
'datetime_sort': e.strftime(fmt),
Expand Down Expand Up @@ -144,6 +207,7 @@ def dates(bot, mask, target, args):
if start < utc.localize(now) or start > utc.localize(then):
continue

found += 1
data.append({
'datetime': start.astimezone(timezoneEF).strftime(fmt),
'datetime_sort':
Expand All @@ -167,15 +231,66 @@ def dates(bot, mask, target, args):
k['datetime_sort'], "%d.%m.%Y %H:%M").timetuple()))

"""
spit out all events in database into IRC. If there were no
events, print some message about this...
Spit out all events in database into IRC. Suppress duplicate lines
from nonconforming ics files. Add message on announcing events. If
there were no events, print some message about this...
"""

if found > 0 and announce > 0:
bot.privmsg(target, "Please notice the next following event(s):")

last_output = None
for ev in data:
bot.privmsg(target, " %s - %s" % (ev['datetime'], ev['info']))
output = " %s - %s" % (ev['datetime'], ev['info'])
if ev['loc']:
output = "%s (%s)" % (output, ev['loc'])

if last_output != output:
last_output = output
bot.privmsg(target, output)

if found == 0:
bot.privmsg(target, "No dates during the next week")
if found == 0 and announce == 0:
bot.privmsg(
target,
"No dates during the next %d days" % config['list_days']
)

except KeyError:
bot.privmsg(target, "Error while retrieving rss data")
bot.privmsg(target, "Error while retrieving dates data")
raise Exception()


@asyncio.coroutine
def _update_cache(bot):
"""Update cached ical file"""

config = dates_configuration(bot)

try:
"""Request the ical file."""
with aiohttp.Timeout(10):
with aiohttp.ClientSession(loop=bot.loop) as session:
resp = yield from session.get(config['url'])
if resp.status == 200:
"""Get text content from http request."""
r = yield from resp.text()
else:
bot.privmsg(bot.config.autojoins[0],
"Error while retrieving calendar data")
raise Exception()

except Exception as e:
bot.log.error(e)
if cron == 0:
bot.privmsg(bot.config.autojoins[0],
"Error while retrieving calendar data")

try:
""" Save ical cache to disk """
cache = open(config['cache'], "w")
cache.truncate(0)
cache.write('%s' % r)
cache.close()

except OSError as e:
bot.log.error(e)
39 changes: 21 additions & 18 deletions plugins/parking.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ def parking(bot, mask, target, args):
if config['url'] == "parking_url":
return "I don't have your parking url!"

bot.privmsg(target, 'Parkhausbelegung:')

with aiohttp.Timeout(10):
with aiohttp.ClientSession(loop=bot.loop) as session:
resp = yield from session.get(config['url'])
Expand All @@ -29,20 +27,25 @@ def parking(bot, mask, target, args):
raise Exception()
r = yield from resp.read()

root = ET.fromstring(r)

"""Sort XML by element longname"""
root[:] = sorted(root, key=lambda key: key.findtext("longname"))

for lot in root.findall('ph'):
bot.privmsg(
target,
" {name:32}{use:3} von {max:3} frei".format(
name=lot.find('longname').text,
use=(
int(lot.find('kapazitaet').text) -
int(lot.find('belegung').text)
),
max=int(lot.find('kapazitaet').text)
)
try:
root = ET.fromstring(r)

"""Sort XML by element longname"""
root[:] = sorted(root, key=lambda key: key.findtext("longname"))

bot.privmsg(target, 'Parkhausbelegung:')

for lot in root.findall('ph'):
bot.privmsg(
target,
" {name:32}{use:3} von {max:3} frei".format(
name=lot.find('longname').text,
use=(
int(lot.find('kapazitaet').text) -
int(lot.find('belegung').text)
),
max=int(lot.find('kapazitaet').text)
)
)
except Exception:
bot.privmsg(target, "Error while parsing parking data")
6 changes: 5 additions & 1 deletion plugins/rss.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,11 @@ def _rss_process_feed(bot, target, feedname, config, number_of_entries=-1):
"""Etag was used in request and noting changed."""
return
else:
bot.privmsg(target, "Error while retrieving rss data")
if number_of_entries == -1:
bot.log.error("Error while retrieving rss data")
else:
bot.privmsg(target, "Error while retrieving rss data")

raise Exception()

try:
Expand Down
Loading

0 comments on commit 89e0013

Please sign in to comment.