marvin_actions

Make actions for Marvin, one function for each action.

  1#! /usr/bin/env python3
  2# -*- coding: utf-8 -*-
  3
  4"""
  5Make actions for Marvin, one function for each action.
  6"""
  7from urllib.parse import quote_plus
  8import calendar
  9import datetime
 10import json
 11import logging
 12import random
 13import requests
 14
 15
 16LOG = logging.getLogger("action")
 17
 18def getAllActions():
 19    """
 20    Return all actions in an array.
 21    """
 22    return [
 23        marvinExplainShell,
 24        marvinGoogle,
 25        marvinLunch,
 26        marvinVideoOfToday,
 27        marvinWhoIs,
 28        marvinHelp,
 29        marvinSource,
 30        marvinBudord,
 31        marvinQuote,
 32        marvinWeather,
 33        marvinSun,
 34        marvinSayHi,
 35        marvinSmile,
 36        marvinStrip,
 37        marvinTimeToBBQ,
 38        marvinNameday,
 39        marvinUptime,
 40        marvinStream,
 41        marvinPrinciple,
 42        marvinJoke,
 43        marvinCommit
 44    ]
 45
 46
 47# Load all strings from file
 48with open("marvin_strings.json", encoding="utf-8") as f:
 49    STRINGS = json.load(f)
 50
 51
 52def getString(key, key1=None):
 53    """
 54    Get a string from the string database.
 55    """
 56    data = STRINGS[key]
 57    if isinstance(data, list):
 58        res = data[random.randint(0, len(data) - 1)]
 59    elif isinstance(data, dict):
 60        if key1 is None:
 61            res = data
 62        else:
 63            res = data[key1]
 64            if isinstance(res, list):
 65                res = res[random.randint(0, len(res) - 1)]
 66    elif isinstance(data, str):
 67        res = data
 68    else:
 69        raise ValueError("Unsupported datatype in strings.json")
 70
 71    return res
 72
 73
 74def marvinSmile(row):
 75    """
 76    Make Marvin smile.
 77    """
 78    msg = None
 79    if any(r in row for r in ["smile", "le", "skratta", "smilies"]):
 80        msg = getString("smile")
 81    return msg
 82
 83
 84def wordsAfterKeyWords(words, keyWords):
 85    """
 86    Return all items in the words list after the first occurence
 87    of an item in the keyWords list.
 88    """
 89    kwIndex = []
 90    for kw in keyWords:
 91        if kw in words:
 92            kwIndex.append(words.index(kw))
 93
 94    if not kwIndex:
 95        return None
 96
 97    return words[min(kwIndex)+1:]
 98
 99
100def marvinGoogle(row):
101    """
102    Let Marvin present an url to google.
103    """
104    query = wordsAfterKeyWords(row, ["google", "googla"])
105    if not query:
106        return None
107
108    searchStr = " ".join(query)
109    url = "https://www.google.se/search?q="
110    url += quote_plus(searchStr)
111    msg = getString("google")
112    return msg.format(url)
113
114
115def marvinExplainShell(row):
116    """
117    Let Marvin present an url to the service explain shell to
118    explain a shell command.
119    """
120    query = wordsAfterKeyWords(row, ["explain", "förklara"])
121    if not query:
122        return None
123    cmd = " ".join(query)
124    url = "https://explainshell.com/explain?cmd="
125    url += quote_plus(cmd, "/:")
126    msg = getString("explainShell")
127    return msg.format(url)
128
129
130def marvinSource(row):
131    """
132    State message about sourcecode.
133    """
134    msg = None
135    if any(r in row for r in ["källkod", "source"]):
136        msg = getString("source")
137
138    return msg
139
140
141def marvinBudord(row):
142    """
143    What are the budord for Marvin?
144    """
145    msg = None
146    if any(r in row for r in ["budord", "stentavla"]):
147        if any(r in row for r in ["#1", "1"]):
148            msg = getString("budord", "#1")
149        elif any(r in row for r in ["#2", "2"]):
150            msg = getString("budord", "#2")
151        elif any(r in row for r in ["#3", "3"]):
152            msg = getString("budord", "#3")
153        elif any(r in row for r in ["#4", "4"]):
154            msg = getString("budord", "#4")
155        elif any(r in row for r in ["#5", "5"]):
156            msg = getString("budord", "#5")
157
158    return msg
159
160
161def marvinQuote(row):
162    """
163    Make a quote.
164    """
165    msg = None
166    if any(r in row for r in ["quote", "citat", "filosofi", "filosofera"]):
167        msg = getString("hitchhiker")
168
169    return msg
170
171
172def videoOfToday():
173    """
174    Check what day it is and provide a url to a suitable video together with a greeting.
175    """
176    weekday = datetime.date.today().strftime("%A")
177    day = getString("video-of-today", weekday)
178    msg = day.get("message")
179
180    if day:
181        msg += " En passande video är " + day.get("url")
182    else:
183        msg += " Jag har ännu ingen passande video för denna dagen."
184
185    return msg
186
187
188def marvinVideoOfToday(row):
189    """
190    Show the video of today.
191    """
192    msg = None
193    if any(r in row for r in ["idag", "dagens"]):
194        if any(r in row for r in ["video", "youtube", "tube"]):
195            msg = videoOfToday()
196
197    return msg
198
199
200def marvinWhoIs(row):
201    """
202    Who is Marvin.
203    """
204    msg = None
205    if all(r in row for r in ["vem", "är"]):
206        msg = getString("whois")
207
208    return msg
209
210
211def marvinHelp(row):
212    """
213    Provide a menu.
214    """
215    msg = None
216    if any(r in row for r in ["hjälp", "help", "menu", "meny"]):
217        msg = getString("menu")
218
219    return msg
220
221
222def marvinSayHi(row):
223    """
224    Say hi with a nice message.
225    """
226    msg = None
227    if any(r in row for r in [
228            "snälla", "hej", "tjena", "morsning", "morrn", "mår", "hallå",
229            "halloj", "läget", "snäll", "duktig", "träna", "träning",
230            "utbildning", "tack", "tacka", "tackar", "tacksam"
231    ]):
232        smile = getString("smile")
233        hello = getString("hello")
234        friendly = getString("friendly")
235        msg = f"{smile} {hello} {friendly}"
236
237    return msg
238
239
240def marvinLunch(row):
241    """
242    Help decide where to eat.
243    """
244    lunchOptions = {
245        'stan centrum karlskrona kna': 'karlskrona',
246        'ängelholm angelholm engelholm': 'angelholm',
247        'hässleholm hassleholm': 'hassleholm',
248        'malmö malmo malmoe': 'malmo',
249        'göteborg goteborg gbg': 'goteborg'
250    }
251
252    data = getString("lunch")
253
254    if any(r in row for r in ["lunch", "mat", "äta", "luncha"]):
255        places = data.get("location").get("bth")
256        for keys, value in lunchOptions.items():
257            if any(r in row for r in keys.split(" ")):
258                places = data.get("location").get(value)
259
260        lunchStr = getString("lunch", "message")
261        return lunchStr.format(places[random.randint(0, len(places) - 1)])
262
263    return None
264
265
266def marvinSun(row):
267    """
268    Check when the sun goes up and down.
269    """
270    msg = None
271    if any(r in row for r in ["sol", "solen", "solnedgång", "soluppgång", "sun"]):
272        try:
273            url = getString("sun", "url")
274            r = requests.get(url, timeout=5)
275            sundata = r.json()
276            # Formats the time from the response to HH:mm instead of hh:mm:ss
277            sunrise = sundata["results"]["sunrise"].split()[0][:-3]
278            sunset = sundata["results"]["sunset"].split()[0][:-3]
279            # The api uses AM/PM notation, this converts the sunset to 12 hour time
280            sunsetHour = int(sunset.split(":")[0]) + 12
281            sunset = str(sunsetHour) + sunset[-3:]
282            msg = getString("sun", "msg").format(sunrise, sunset)
283            return msg
284
285        except Exception as e:
286            LOG.error("Failed to get sun times: %s", e)
287            return getString("sun", "error")
288
289    return msg
290
291
292def marvinWeather(row):
293    """
294    Check what the weather prognosis looks like.
295    """
296    msg = ""
297    if any(r in row for r in ["väder", "vädret", "prognos", "prognosen", "smhi"]):
298        forecast = ""
299        observation = ""
300
301        try:
302            station_req = requests.get(getString("smhi", "station_url"), timeout=5)
303            weather_code:int = int(station_req.json().get("value")[0].get("value"))
304
305            weather_codes_req = requests.get(getString("smhi", "weather_codes_url"), timeout=5)
306            weather_codes_arr: list = weather_codes_req.json().get("entry")
307
308            current_weather_req = requests.get(getString("smhi", "current_weather_url"), timeout=5)
309            current_w_data: list = current_weather_req.json().get("timeSeries")[0].get("parameters")
310
311            for curr_w in current_w_data:
312                if curr_w.get("name") == "t":
313                    forecast = curr_w.get("values")[0]
314
315            for code in weather_codes_arr:
316                if code.get("key") == weather_code:
317                    observation = code.get("value")
318
319            msg = f"Karlskrona just nu: {forecast} °C. {observation}."
320
321        except Exception as e:
322            LOG.error("Failed to get weather: %s", e)
323            msg: str = getString("smhi", "failed")
324
325    return msg
326
327
328def marvinStrip(row):
329    """
330    Get a comic strip.
331    """
332    msg = None
333    if any(r in row for r in ["strip", "comic", "nöje", "paus"]):
334        msg = commitStrip(randomize=any(r in row for r in ["rand", "random", "slump", "lucky"]))
335    return msg
336
337
338def commitStrip(randomize=False):
339    """
340    Latest or random comic strip from CommitStrip.
341    """
342    msg = getString("commitstrip", "message")
343
344    if randomize:
345        first = getString("commitstrip", "first")
346        last = getString("commitstrip", "last")
347        rand = random.randint(first, last)
348        url = getString("commitstrip", "urlPage") + str(rand)
349    else:
350        url = getString("commitstrip", "url")
351
352    return msg.format(url=url)
353
354
355def marvinTimeToBBQ(row):
356    """
357    Calcuate the time to next barbecue and print a appropriate msg
358    """
359    msg = None
360    if any(r in row for r in ["grilla", "grill", "grillcon", "bbq"]):
361        url = getString("barbecue", "url")
362        nextDate = nextBBQ()
363        today = datetime.date.today()
364        daysRemaining = (nextDate - today).days
365
366        if daysRemaining == 0:
367            msg = getString("barbecue", "today")
368        elif daysRemaining == 1:
369            msg = getString("barbecue", "tomorrow")
370        elif 1 < daysRemaining < 14:
371            msg = getString("barbecue", "week") % nextDate
372        elif 14 < daysRemaining < 200:
373            msg = getString("barbecue", "base") % nextDate
374        else:
375            msg = getString("barbecue", "eternity") % nextDate
376
377        msg = url + ". " + msg
378    return msg
379
380def nextBBQ():
381    """
382    Calculate the next grillcon date after today
383    """
384
385    MAY = 5
386    SEPTEMBER = 9
387
388    after = datetime.date.today()
389    spring = thirdFridayIn(after.year, MAY)
390    if after <= spring:
391        return spring
392
393    autumn = thirdFridayIn(after.year, SEPTEMBER)
394    if after <= autumn:
395        return autumn
396
397    return thirdFridayIn(after.year + 1, MAY)
398
399
400def thirdFridayIn(y, m):
401    """
402    Get the third Friday in a given month and year
403    """
404    THIRD = 2
405    FRIDAY = -1
406
407    # Start the weeks on saturday to prevent fridays from previous month
408    cal = calendar.Calendar(firstweekday=calendar.SATURDAY)
409
410    # Return the friday in the third week
411    return cal.monthdatescalendar(y, m)[THIRD][FRIDAY]
412
413
414def marvinNameday(row):
415    """
416    Check current nameday
417    """
418    msg = None
419    if any(r in row for r in ["nameday", "namnsdag"]):
420        try:
421            now = datetime.datetime.now()
422            raw_url = "https://api.dryg.net/dagar/v2.1/{year}/{month}/{day}"
423            url = raw_url.format(year=now.year, month=now.month, day=now.day)
424            r = requests.get(url, timeout=5)
425            nameday_data = r.json()
426            names = nameday_data["dagar"][0]["namnsdag"]
427            parsed_names = formatNames(names)
428            if names:
429                msg = getString("nameday", "somebody").format(parsed_names)
430            else:
431                msg = getString("nameday", "nobody")
432        except Exception as e:
433            LOG.error("Failed to get nameday: %s", e)
434            msg = getString("nameday", "error")
435    return msg
436
437def formatNames(names):
438    """
439    Parses namedata from nameday API
440    """
441    if len(names) > 1:
442        return " och ".join([", ".join(names[:-1])] + names[-1:])
443    return "".join(names)
444
445def marvinUptime(row):
446    """
447    Display info about uptime tournament
448    """
449    msg = None
450    if "uptime" in row:
451        msg = getString("uptime", "info")
452    return msg
453
454def marvinStream(row):
455    """
456    Display info about stream
457    """
458    msg = None
459    if any(r in row for r in ["stream", "streama", "ström", "strömma"]):
460        msg = getString("stream", "info")
461    return msg
462
463def marvinPrinciple(row):
464    """
465    Display one selected software principle, or provide one as random
466    """
467    msg = None
468    if any(r in row for r in ["principle", "princip", "principer"]):
469        principles = getString("principle")
470        principleKeys = list(principles.keys())
471        matchedKeys = [k for k in row if k in principleKeys]
472        if matchedKeys:
473            msg = principles[matchedKeys.pop()]
474        else:
475            msg = principles[random.choice(principleKeys)]
476    return msg
477
478def getJoke():
479    """
480    Retrieves joke from api.chucknorris.io/jokes/random?category=dev
481    """
482    try:
483        url = getString("joke", "url")
484        r = requests.get(url, timeout=5)
485        joke_data = r.json()
486        return joke_data["value"]
487    except Exception as e:
488        LOG.error("Failed to get joke: %s", e)
489        return getString("joke", "error")
490
491def marvinJoke(row):
492    """
493    Display a random Chuck Norris joke
494    """
495    msg = None
496    if any(r in row for r in ["joke", "skämt", "chuck norris", "chuck", "norris"]):
497        msg = getJoke()
498    return msg
499
500def getCommit():
501    """
502    Retrieves random commit message from whatthecommit.com/index.html
503    """
504    try:
505        url = getString("commit", "url")
506        r = requests.get(url, timeout=5)
507        res = r.text.strip()
508        msg = f"Använd detta meddelandet: '{res}'"
509        return msg
510    except Exception as e:
511        LOG.error("Failed to get commit message: %s", e)
512        return getString("commit", "error")
513
514def marvinCommit(row):
515    """
516    Display a random commit message
517    """
518    msg = None
519    if any(r in row for r in ["commit", "-m"]):
520        msg = getCommit()
521    return msg
LOG = <Logger action (WARNING)>
def getAllActions():
19def getAllActions():
20    """
21    Return all actions in an array.
22    """
23    return [
24        marvinExplainShell,
25        marvinGoogle,
26        marvinLunch,
27        marvinVideoOfToday,
28        marvinWhoIs,
29        marvinHelp,
30        marvinSource,
31        marvinBudord,
32        marvinQuote,
33        marvinWeather,
34        marvinSun,
35        marvinSayHi,
36        marvinSmile,
37        marvinStrip,
38        marvinTimeToBBQ,
39        marvinNameday,
40        marvinUptime,
41        marvinStream,
42        marvinPrinciple,
43        marvinJoke,
44        marvinCommit
45    ]

Return all actions in an array.

def getString(key, key1=None):
53def getString(key, key1=None):
54    """
55    Get a string from the string database.
56    """
57    data = STRINGS[key]
58    if isinstance(data, list):
59        res = data[random.randint(0, len(data) - 1)]
60    elif isinstance(data, dict):
61        if key1 is None:
62            res = data
63        else:
64            res = data[key1]
65            if isinstance(res, list):
66                res = res[random.randint(0, len(res) - 1)]
67    elif isinstance(data, str):
68        res = data
69    else:
70        raise ValueError("Unsupported datatype in strings.json")
71
72    return res

Get a string from the string database.

def marvinSmile(row):
75def marvinSmile(row):
76    """
77    Make Marvin smile.
78    """
79    msg = None
80    if any(r in row for r in ["smile", "le", "skratta", "smilies"]):
81        msg = getString("smile")
82    return msg

Make Marvin smile.

def wordsAfterKeyWords(words, keyWords):
85def wordsAfterKeyWords(words, keyWords):
86    """
87    Return all items in the words list after the first occurence
88    of an item in the keyWords list.
89    """
90    kwIndex = []
91    for kw in keyWords:
92        if kw in words:
93            kwIndex.append(words.index(kw))
94
95    if not kwIndex:
96        return None
97
98    return words[min(kwIndex)+1:]

Return all items in the words list after the first occurence of an item in the keyWords list.

def marvinGoogle(row):
101def marvinGoogle(row):
102    """
103    Let Marvin present an url to google.
104    """
105    query = wordsAfterKeyWords(row, ["google", "googla"])
106    if not query:
107        return None
108
109    searchStr = " ".join(query)
110    url = "https://www.google.se/search?q="
111    url += quote_plus(searchStr)
112    msg = getString("google")
113    return msg.format(url)

Let Marvin present an url to google.

def marvinExplainShell(row):
116def marvinExplainShell(row):
117    """
118    Let Marvin present an url to the service explain shell to
119    explain a shell command.
120    """
121    query = wordsAfterKeyWords(row, ["explain", "förklara"])
122    if not query:
123        return None
124    cmd = " ".join(query)
125    url = "https://explainshell.com/explain?cmd="
126    url += quote_plus(cmd, "/:")
127    msg = getString("explainShell")
128    return msg.format(url)

Let Marvin present an url to the service explain shell to explain a shell command.

def marvinSource(row):
131def marvinSource(row):
132    """
133    State message about sourcecode.
134    """
135    msg = None
136    if any(r in row for r in ["källkod", "source"]):
137        msg = getString("source")
138
139    return msg

State message about sourcecode.

def marvinBudord(row):
142def marvinBudord(row):
143    """
144    What are the budord for Marvin?
145    """
146    msg = None
147    if any(r in row for r in ["budord", "stentavla"]):
148        if any(r in row for r in ["#1", "1"]):
149            msg = getString("budord", "#1")
150        elif any(r in row for r in ["#2", "2"]):
151            msg = getString("budord", "#2")
152        elif any(r in row for r in ["#3", "3"]):
153            msg = getString("budord", "#3")
154        elif any(r in row for r in ["#4", "4"]):
155            msg = getString("budord", "#4")
156        elif any(r in row for r in ["#5", "5"]):
157            msg = getString("budord", "#5")
158
159    return msg

What are the budord for Marvin?

def marvinQuote(row):
162def marvinQuote(row):
163    """
164    Make a quote.
165    """
166    msg = None
167    if any(r in row for r in ["quote", "citat", "filosofi", "filosofera"]):
168        msg = getString("hitchhiker")
169
170    return msg

Make a quote.

def videoOfToday():
173def videoOfToday():
174    """
175    Check what day it is and provide a url to a suitable video together with a greeting.
176    """
177    weekday = datetime.date.today().strftime("%A")
178    day = getString("video-of-today", weekday)
179    msg = day.get("message")
180
181    if day:
182        msg += " En passande video är " + day.get("url")
183    else:
184        msg += " Jag har ännu ingen passande video för denna dagen."
185
186    return msg

Check what day it is and provide a url to a suitable video together with a greeting.

def marvinVideoOfToday(row):
189def marvinVideoOfToday(row):
190    """
191    Show the video of today.
192    """
193    msg = None
194    if any(r in row for r in ["idag", "dagens"]):
195        if any(r in row for r in ["video", "youtube", "tube"]):
196            msg = videoOfToday()
197
198    return msg

Show the video of today.

def marvinWhoIs(row):
201def marvinWhoIs(row):
202    """
203    Who is Marvin.
204    """
205    msg = None
206    if all(r in row for r in ["vem", "är"]):
207        msg = getString("whois")
208
209    return msg

Who is Marvin.

def marvinHelp(row):
212def marvinHelp(row):
213    """
214    Provide a menu.
215    """
216    msg = None
217    if any(r in row for r in ["hjälp", "help", "menu", "meny"]):
218        msg = getString("menu")
219
220    return msg

Provide a menu.

def marvinSayHi(row):
223def marvinSayHi(row):
224    """
225    Say hi with a nice message.
226    """
227    msg = None
228    if any(r in row for r in [
229            "snälla", "hej", "tjena", "morsning", "morrn", "mår", "hallå",
230            "halloj", "läget", "snäll", "duktig", "träna", "träning",
231            "utbildning", "tack", "tacka", "tackar", "tacksam"
232    ]):
233        smile = getString("smile")
234        hello = getString("hello")
235        friendly = getString("friendly")
236        msg = f"{smile} {hello} {friendly}"
237
238    return msg

Say hi with a nice message.

def marvinLunch(row):
241def marvinLunch(row):
242    """
243    Help decide where to eat.
244    """
245    lunchOptions = {
246        'stan centrum karlskrona kna': 'karlskrona',
247        'ängelholm angelholm engelholm': 'angelholm',
248        'hässleholm hassleholm': 'hassleholm',
249        'malmö malmo malmoe': 'malmo',
250        'göteborg goteborg gbg': 'goteborg'
251    }
252
253    data = getString("lunch")
254
255    if any(r in row for r in ["lunch", "mat", "äta", "luncha"]):
256        places = data.get("location").get("bth")
257        for keys, value in lunchOptions.items():
258            if any(r in row for r in keys.split(" ")):
259                places = data.get("location").get(value)
260
261        lunchStr = getString("lunch", "message")
262        return lunchStr.format(places[random.randint(0, len(places) - 1)])
263
264    return None

Help decide where to eat.

def marvinSun(row):
267def marvinSun(row):
268    """
269    Check when the sun goes up and down.
270    """
271    msg = None
272    if any(r in row for r in ["sol", "solen", "solnedgång", "soluppgång", "sun"]):
273        try:
274            url = getString("sun", "url")
275            r = requests.get(url, timeout=5)
276            sundata = r.json()
277            # Formats the time from the response to HH:mm instead of hh:mm:ss
278            sunrise = sundata["results"]["sunrise"].split()[0][:-3]
279            sunset = sundata["results"]["sunset"].split()[0][:-3]
280            # The api uses AM/PM notation, this converts the sunset to 12 hour time
281            sunsetHour = int(sunset.split(":")[0]) + 12
282            sunset = str(sunsetHour) + sunset[-3:]
283            msg = getString("sun", "msg").format(sunrise, sunset)
284            return msg
285
286        except Exception as e:
287            LOG.error("Failed to get sun times: %s", e)
288            return getString("sun", "error")
289
290    return msg

Check when the sun goes up and down.

def marvinWeather(row):
293def marvinWeather(row):
294    """
295    Check what the weather prognosis looks like.
296    """
297    msg = ""
298    if any(r in row for r in ["väder", "vädret", "prognos", "prognosen", "smhi"]):
299        forecast = ""
300        observation = ""
301
302        try:
303            station_req = requests.get(getString("smhi", "station_url"), timeout=5)
304            weather_code:int = int(station_req.json().get("value")[0].get("value"))
305
306            weather_codes_req = requests.get(getString("smhi", "weather_codes_url"), timeout=5)
307            weather_codes_arr: list = weather_codes_req.json().get("entry")
308
309            current_weather_req = requests.get(getString("smhi", "current_weather_url"), timeout=5)
310            current_w_data: list = current_weather_req.json().get("timeSeries")[0].get("parameters")
311
312            for curr_w in current_w_data:
313                if curr_w.get("name") == "t":
314                    forecast = curr_w.get("values")[0]
315
316            for code in weather_codes_arr:
317                if code.get("key") == weather_code:
318                    observation = code.get("value")
319
320            msg = f"Karlskrona just nu: {forecast} °C. {observation}."
321
322        except Exception as e:
323            LOG.error("Failed to get weather: %s", e)
324            msg: str = getString("smhi", "failed")
325
326    return msg

Check what the weather prognosis looks like.

def marvinStrip(row):
329def marvinStrip(row):
330    """
331    Get a comic strip.
332    """
333    msg = None
334    if any(r in row for r in ["strip", "comic", "nöje", "paus"]):
335        msg = commitStrip(randomize=any(r in row for r in ["rand", "random", "slump", "lucky"]))
336    return msg

Get a comic strip.

def commitStrip(randomize=False):
339def commitStrip(randomize=False):
340    """
341    Latest or random comic strip from CommitStrip.
342    """
343    msg = getString("commitstrip", "message")
344
345    if randomize:
346        first = getString("commitstrip", "first")
347        last = getString("commitstrip", "last")
348        rand = random.randint(first, last)
349        url = getString("commitstrip", "urlPage") + str(rand)
350    else:
351        url = getString("commitstrip", "url")
352
353    return msg.format(url=url)

Latest or random comic strip from CommitStrip.

def marvinTimeToBBQ(row):
356def marvinTimeToBBQ(row):
357    """
358    Calcuate the time to next barbecue and print a appropriate msg
359    """
360    msg = None
361    if any(r in row for r in ["grilla", "grill", "grillcon", "bbq"]):
362        url = getString("barbecue", "url")
363        nextDate = nextBBQ()
364        today = datetime.date.today()
365        daysRemaining = (nextDate - today).days
366
367        if daysRemaining == 0:
368            msg = getString("barbecue", "today")
369        elif daysRemaining == 1:
370            msg = getString("barbecue", "tomorrow")
371        elif 1 < daysRemaining < 14:
372            msg = getString("barbecue", "week") % nextDate
373        elif 14 < daysRemaining < 200:
374            msg = getString("barbecue", "base") % nextDate
375        else:
376            msg = getString("barbecue", "eternity") % nextDate
377
378        msg = url + ". " + msg
379    return msg

Calcuate the time to next barbecue and print a appropriate msg

def nextBBQ():
381def nextBBQ():
382    """
383    Calculate the next grillcon date after today
384    """
385
386    MAY = 5
387    SEPTEMBER = 9
388
389    after = datetime.date.today()
390    spring = thirdFridayIn(after.year, MAY)
391    if after <= spring:
392        return spring
393
394    autumn = thirdFridayIn(after.year, SEPTEMBER)
395    if after <= autumn:
396        return autumn
397
398    return thirdFridayIn(after.year + 1, MAY)

Calculate the next grillcon date after today

def thirdFridayIn(y, m):
401def thirdFridayIn(y, m):
402    """
403    Get the third Friday in a given month and year
404    """
405    THIRD = 2
406    FRIDAY = -1
407
408    # Start the weeks on saturday to prevent fridays from previous month
409    cal = calendar.Calendar(firstweekday=calendar.SATURDAY)
410
411    # Return the friday in the third week
412    return cal.monthdatescalendar(y, m)[THIRD][FRIDAY]

Get the third Friday in a given month and year

def marvinNameday(row):
415def marvinNameday(row):
416    """
417    Check current nameday
418    """
419    msg = None
420    if any(r in row for r in ["nameday", "namnsdag"]):
421        try:
422            now = datetime.datetime.now()
423            raw_url = "https://api.dryg.net/dagar/v2.1/{year}/{month}/{day}"
424            url = raw_url.format(year=now.year, month=now.month, day=now.day)
425            r = requests.get(url, timeout=5)
426            nameday_data = r.json()
427            names = nameday_data["dagar"][0]["namnsdag"]
428            parsed_names = formatNames(names)
429            if names:
430                msg = getString("nameday", "somebody").format(parsed_names)
431            else:
432                msg = getString("nameday", "nobody")
433        except Exception as e:
434            LOG.error("Failed to get nameday: %s", e)
435            msg = getString("nameday", "error")
436    return msg

Check current nameday

def formatNames(names):
438def formatNames(names):
439    """
440    Parses namedata from nameday API
441    """
442    if len(names) > 1:
443        return " och ".join([", ".join(names[:-1])] + names[-1:])
444    return "".join(names)

Parses namedata from nameday API

def marvinUptime(row):
446def marvinUptime(row):
447    """
448    Display info about uptime tournament
449    """
450    msg = None
451    if "uptime" in row:
452        msg = getString("uptime", "info")
453    return msg

Display info about uptime tournament

def marvinStream(row):
455def marvinStream(row):
456    """
457    Display info about stream
458    """
459    msg = None
460    if any(r in row for r in ["stream", "streama", "ström", "strömma"]):
461        msg = getString("stream", "info")
462    return msg

Display info about stream

def marvinPrinciple(row):
464def marvinPrinciple(row):
465    """
466    Display one selected software principle, or provide one as random
467    """
468    msg = None
469    if any(r in row for r in ["principle", "princip", "principer"]):
470        principles = getString("principle")
471        principleKeys = list(principles.keys())
472        matchedKeys = [k for k in row if k in principleKeys]
473        if matchedKeys:
474            msg = principles[matchedKeys.pop()]
475        else:
476            msg = principles[random.choice(principleKeys)]
477    return msg

Display one selected software principle, or provide one as random

def getJoke():
479def getJoke():
480    """
481    Retrieves joke from api.chucknorris.io/jokes/random?category=dev
482    """
483    try:
484        url = getString("joke", "url")
485        r = requests.get(url, timeout=5)
486        joke_data = r.json()
487        return joke_data["value"]
488    except Exception as e:
489        LOG.error("Failed to get joke: %s", e)
490        return getString("joke", "error")

Retrieves joke from api.chucknorris.io/jokes/random?category=dev

def marvinJoke(row):
492def marvinJoke(row):
493    """
494    Display a random Chuck Norris joke
495    """
496    msg = None
497    if any(r in row for r in ["joke", "skämt", "chuck norris", "chuck", "norris"]):
498        msg = getJoke()
499    return msg

Display a random Chuck Norris joke

def getCommit():
501def getCommit():
502    """
503    Retrieves random commit message from whatthecommit.com/index.html
504    """
505    try:
506        url = getString("commit", "url")
507        r = requests.get(url, timeout=5)
508        res = r.text.strip()
509        msg = f"Använd detta meddelandet: '{res}'"
510        return msg
511    except Exception as e:
512        LOG.error("Failed to get commit message: %s", e)
513        return getString("commit", "error")

Retrieves random commit message from whatthecommit.com/index.html

def marvinCommit(row):
515def marvinCommit(row):
516    """
517    Display a random commit message
518    """
519    msg = None
520    if any(r in row for r in ["commit", "-m"]):
521        msg = getCommit()
522    return msg

Display a random commit message