Passed
Pull Request — master (#70)
by Vilhelm
03:27
created

marvin_actions.marvinNameday()   A

Complexity

Conditions 4

Size

Total Lines 20
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
eloc 16
nop 1
dl 0
loc 20
ccs 16
cts 16
cp 1
crap 4
rs 9.6
c 0
b 0
f 0
1
#! /usr/bin/env python3
2
# -*- coding: utf-8 -*-
3
4
"""
5
Make actions for Marvin, one function for each action.
6
"""
7 1
from urllib.parse import quote_plus
8 1
from urllib.request import urlopen
9 1
import calendar
10 1
import datetime
11 1
import json
12 1
import random
13 1
import requests
14
15 1
from bs4 import BeautifulSoup
16
17
18 1
def 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
        marvinStats,
33
        marvinIrcLog,
34
        marvinListen,
35
        marvinWeather,
36
        marvinSun,
37
        marvinSayHi,
38
        marvinSmile,
39
        marvinStrip,
40
        marvinTimeToBBQ,
41
        marvinBirthday,
42
        marvinNameday,
43
        marvinUptime,
44
        marvinStream,
45
        marvinPrinciple,
46
        marvinJoke,
47
        marvinCommit
48
    ]
49
50
51
# Load all strings from file
52 1
with open("marvin_strings.json", encoding="utf-8") as f:
53 1
    STRINGS = json.load(f)
54
55
# Configuration loaded
56 1
CONFIG = None
57
58 1
def setConfig(config):
59
    """
60
    Keep reference to the loaded configuration.
61
    """
62
    global CONFIG
63
    CONFIG = config
64
65
66 1 View Code Duplication
def getString(key, key1=None):
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
67
    """
68
    Get a string from the string database.
69
    """
70 1
    data = STRINGS[key]
71 1
    if isinstance(data, list):
72 1
        res = data[random.randint(0, len(data) - 1)]
73 1
    elif isinstance(data, dict):
74 1
        if key1 is None:
75 1
            res = data
76
        else:
77 1
            res = data[key1]
78 1
            if isinstance(res, list):
79 1
                res = res[random.randint(0, len(res) - 1)]
80 1
    elif isinstance(data, str):
81 1
        res = data
82
83 1
    return res
0 ignored issues
show
introduced by
The variable res does not seem to be defined for all execution paths.
Loading history...
84
85
86 1
def marvinSmile(row):
87
    """
88
    Make Marvin smile.
89
    """
90 1
    msg = None
91 1
    if any(r in row for r in ["smile", "le", "skratta", "smilies"]):
92 1
        smilie = getString("smile")
93 1
        msg = "{SMILE}".format(SMILE=smilie)
94 1
    return msg
95
96
97 1
def wordsAfterKeyWords(words, keyWords):
98
    """
99
    Return all items in the words list after the first occurence
100
    of an item in the keyWords list.
101
    """
102 1
    kwIndex = []
103 1
    for kw in keyWords:
104 1
        if kw in words:
105 1
            kwIndex.append(words.index(kw))
106
107 1
    if not kwIndex:
108 1
        return None
109
110 1
    return words[min(kwIndex)+1:]
111
112
113 1
def marvinGoogle(row):
114
    """
115
    Let Marvin present an url to google.
116
    """
117 1
    query = wordsAfterKeyWords(row, ["google", "googla"])
118 1
    if not query:
119 1
        return None
120
121 1
    searchStr = " ".join(query)
122 1
    url = "https://www.google.se/search?q="
123 1
    url += quote_plus(searchStr)
124 1
    msg = getString("google")
125 1
    return msg.format(url)
126
127
128 1
def marvinExplainShell(row):
129
    """
130
    Let Marvin present an url to the service explain shell to
131
    explain a shell command.
132
    """
133 1
    query = wordsAfterKeyWords(row, ["explain", "förklara"])
134 1
    if not query:
135 1
        return None
136 1
    cmd = " ".join(query)
137 1
    url = "http://explainshell.com/explain?cmd="
138 1
    url += quote_plus(cmd, "/:")
139 1
    msg = getString("explainShell")
140 1
    return msg.format(url)
141
142
143 1
def marvinSource(row):
144
    """
145
    State message about sourcecode.
146
    """
147 1
    msg = None
148 1
    if any(r in row for r in ["källkod", "source"]):
149 1
        msg = getString("source")
150
151 1
    return msg
152
153
154 1
def marvinBudord(row):
155
    """
156
    What are the budord for Marvin?
157
    """
158 1
    msg = None
159 1
    if any(r in row for r in ["budord", "stentavla"]):
160 1
        if any(r in row for r in ["#1", "1"]):
161 1
            msg = getString("budord", "#1")
162 1
        elif any(r in row for r in ["#2", "2"]):
163 1
            msg = getString("budord", "#2")
164 1
        elif any(r in row for r in ["#3", "3"]):
165 1
            msg = getString("budord", "#3")
166 1
        elif any(r in row for r in ["#4", "4"]):
167 1
            msg = getString("budord", "#4")
168 1
        elif any(r in row for r in ["#5", "5"]):
169 1
            msg = getString("budord", "#5")
170
171 1
    return msg
172
173
174 1
def marvinQuote(row):
175
    """
176
    Make a quote.
177
    """
178 1
    msg = None
179 1
    if any(r in row for r in ["quote", "citat", "filosofi", "filosofera"]):
180 1
        msg = getString("hitchhiker")
181
182 1
    return msg
183
184
185 1
def videoOfToday():
186
    """
187
    Check what day it is and provide a url to a suitable video together with a greeting.
188
    """
189 1
    dayNum = datetime.date.weekday(datetime.date.today()) + 1
190 1
    msg = getString("weekdays", str(dayNum))
191 1
    video = getString("video-of-today", str(dayNum))
192
193 1
    if video:
194 1
        msg += " En passande video är " + video
195
    else:
196
        msg += " Jag har ännu ingen passande video för denna dagen."
197
198 1
    return msg
199
200
201 1
def marvinVideoOfToday(row):
202
    """
203
    Show the video of today.
204
    """
205 1
    msg = None
206 1
    if any(r in row for r in ["idag", "dagens"]):
207 1
        if any(r in row for r in ["video", "youtube", "tube"]):
208 1
            msg = videoOfToday()
209
210 1
    return msg
211
212
213 1
def marvinWhoIs(row):
214
    """
215
    Who is Marvin.
216
    """
217 1
    msg = None
218 1
    if all(r in row for r in ["vem", "är"]):
219 1
        msg = getString("whois")
220
221 1
    return msg
222
223
224 1
def marvinHelp(row):
225
    """
226
    Provide a menu.
227
    """
228 1
    msg = None
229 1
    if any(r in row for r in ["hjälp", "help", "menu", "meny"]):
230 1
        msg = getString("menu")
231
232 1
    return msg
233
234
235 1
def marvinStats(row):
236
    """
237
    Provide a link to the stats.
238
    """
239 1
    msg = None
240 1
    if any(r in row for r in ["stats", "statistik", "ircstats"]):
241 1
        msg = getString("ircstats")
242
243 1
    return msg
244
245
246 1
def marvinIrcLog(row):
247
    """
248
    Provide a link to the irclog
249
    """
250 1
    msg = None
251 1
    if any(r in row for r in ["irc", "irclog", "log", "irclogg", "logg", "historik"]):
252 1
        msg = getString("irclog")
253
254 1
    return msg
255
256
257 1
def marvinSayHi(row):
258
    """
259
    Say hi with a nice message.
260
    """
261 1
    msg = None
262 1
    if any(r in row for r in [
263
            "snälla", "hej", "tjena", "morsning", "morrn", "mår", "hallå",
264
            "halloj", "läget", "snäll", "duktig", "träna", "träning",
265
            "utbildning", "tack", "tacka", "tackar", "tacksam"
266
    ]):
267 1
        smile = getString("smile")
268 1
        hello = getString("hello")
269 1
        friendly = getString("friendly")
270 1
        msg = "{} {} {}".format(smile, hello, friendly)
271
272 1
    return msg
273
274
275 1
def marvinLunch(row):
276
    """
277
    Help decide where to eat.
278
    """
279 1
    lunchOptions = {
280
        'stan centrum karlskrona kna': 'lunch-karlskrona',
281
        'ängelholm angelholm engelholm': 'lunch-angelholm',
282
        'hässleholm hassleholm': 'lunch-hassleholm',
283
        'malmö malmo malmoe': 'lunch-malmo',
284
        'göteborg goteborg gbg': 'lunch-goteborg'
285
    }
286
287 1
    if any(r in row for r in ["lunch", "mat", "äta", "luncha"]):
288 1
        lunchStr = getString('lunch-message')
289
290 1
        for keys, value in lunchOptions.items():
291 1
            if any(r in row for r in keys.split(" ")):
292 1
                return lunchStr.format(getString(value))
293
294 1
        return lunchStr.format(getString('lunch-bth'))
295
296 1
    return None
297
298
299 1
def marvinListen(row):
300
    """
301
    Return music last listened to.
302
    """
303
    msg = None
304
    if any(r in row for r in ["lyssna", "lyssnar", "musik"]):
305
306
        if not CONFIG["lastfm"]:
307
            return getString("listen", "disabled")
308
309
        url = "http://ws.audioscrobbler.com/2.0/"
310
311
        try:
312
            params = dict(
313
                method="user.getrecenttracks",
314
                user=CONFIG["lastfm"]["user"],
315
                api_key=CONFIG["lastfm"]["apikey"],
316
                format="json",
317
                limit="1"
318
            )
319
320
            resp = requests.get(url=url, params=params, timeout=5)
321
            data = json.loads(resp.text)
322
323
            artist = data["recenttracks"]["track"][0]["artist"]["#text"]
324
            title = data["recenttracks"]["track"][0]["name"]
325
            link = data["recenttracks"]["track"][0]["url"]
326
327
            msg = getString("listen", "success").format(artist=artist, title=title, link=link)
328
329
        except Exception:
330
            msg = getString("listen", "failed")
331
332
    return msg
333
334
335 1
def marvinSun(row):
336
    """
337
    Check when the sun goes up and down.
338
    """
339
    msg = None
340
    if any(r in row for r in ["sol", "solen", "solnedgång", "soluppgång"]):
341
        try:
342
            soup = BeautifulSoup(urlopen('http://www.timeanddate.com/sun/sweden/jonkoping'))
343
            spans = soup.find_all("span", {"class": "three"})
344
            sunrise = spans[0].text
345
            sunset = spans[1].text
346
            msg = getString("sun").format(sunrise, sunset)
347
348
        except Exception:
349
            msg = getString("sun-no")
350
351
    return msg
352
353
354 1
def marvinWeather(row):
355
    """
356
    Check what the weather prognosis looks like.
357
    """
358
    msg = None
359
    if any(r in row for r in ["väder", "vädret", "prognos", "prognosen", "smhi"]):
360
        url = getString("smhi", "url")
361
        try:
362
            soup = BeautifulSoup(urlopen(url))
363
            msg = "{}. {}. {}".format(
364
                soup.h1.text,
365
                soup.h4.text,
366
                soup.h4.findNextSibling("p").text
367
            )
368
369
        except Exception:
370
            msg = getString("smhi", "failed")
371
372
    return msg
373
374
375 1
def marvinStrip(row):
376
    """
377
    Get a comic strip.
378
    """
379 1
    msg = None
380 1
    if any(r in row for r in ["strip", "comic", "nöje", "paus"]):
381 1
        msg = commitStrip(randomize=any(r in row for r in ["rand", "random", "slump", "lucky"]))
382 1
    return msg
383
384
385 1
def commitStrip(randomize=False):
386
    """
387
    Latest or random comic strip from CommitStrip.
388
    """
389 1
    msg = getString("commitstrip", "message")
390
391 1
    if randomize:
392 1
        first = getString("commitstrip", "first")
393 1
        last = getString("commitstrip", "last")
394 1
        rand = random.randint(first, last)
395 1
        url = getString("commitstrip", "urlPage") + str(rand)
396
    else:
397 1
        url = getString("commitstrip", "url")
398
399 1
    return msg.format(url=url)
400
401
402 1
def marvinTimeToBBQ(row):
403
    """
404
    Calcuate the time to next barbecue and print a appropriate msg
405
    """
406 1
    msg = None
407 1
    if any(r in row for r in ["grilla", "grill", "grillcon", "bbq"]):
408 1
        url = getString("barbecue", "url")
409 1
        nextDate = nextBBQ()
410 1
        today = datetime.date.today()
411 1
        daysRemaining = (nextDate - today).days
412
413 1
        if daysRemaining == 0:
414 1
            msg = getString("barbecue", "today")
415 1
        elif daysRemaining == 1:
416 1
            msg = getString("barbecue", "tomorrow")
417 1
        elif 1 < daysRemaining < 14:
418 1
            msg = getString("barbecue", "week") % nextDate
419 1
        elif 14 < daysRemaining < 200:
420 1
            msg = getString("barbecue", "base") % nextDate
421
        else:
422 1
            msg = getString("barbecue", "eternity") % nextDate
423
424 1
        msg = url + ". " + msg
425 1
    return msg
426
427 1
def nextBBQ():
428
    """
429
    Calculate the next grillcon date after today
430
    """
431
432 1
    MAY = 5
433 1
    SEPTEMBER = 9
434
435 1
    after = datetime.date.today()
436 1
    spring = thirdFridayIn(after.year, MAY)
437 1
    if after <= spring:
438 1
        return spring
439
440 1
    autumn = thirdFridayIn(after.year, SEPTEMBER)
441 1
    if after <= autumn:
442 1
        return autumn
443
444 1
    return thirdFridayIn(after.year + 1, MAY)
445
446
447 1
def thirdFridayIn(y, m):
448
    """
449
    Get the third Friday in a given month and year
450
    """
451 1
    THIRD = 2
452 1
    FRIDAY = -1
453
454
    # Start the weeks on saturday to prevent fridays from previous month
455 1
    cal = calendar.Calendar(firstweekday=calendar.SATURDAY)
456
457
    # Return the friday in the third week
458 1
    return cal.monthdatescalendar(y, m)[THIRD][FRIDAY]
459
460
461 1
def marvinBirthday(row):
462
    """
463
    Check birthday info
464
    """
465
    msg = None
466
    if any(r in row for r in ["birthday", "födelsedag"]):
467
        try:
468
            url = getString("birthday", "url")
469
            soup = BeautifulSoup(urlopen(url), "html.parser")
470
            my_list = list()
471
472
            for ana in soup.findAll('a'):
473
                if ana.parent.name == 'strong':
474
                    my_list.append(ana.getText())
475
476
            my_list.pop()
477
            my_strings = ', '.join(my_list)
478
            if not my_strings:
479
                msg = getString("birthday", "nobody")
480
            else:
481
                msg = getString("birthday", "somebody").format(my_strings)
482
483
        except Exception:
484
            msg = getString("birthday", "error")
485
486
    return msg
487
488 1
def marvinNameday(row):
489
    """
490
    Check current nameday
491
    """
492 1
    msg = None
493 1
    if any(r in row for r in ["nameday", "namnsdag"]):
494 1
        try:
495 1
            now = datetime.datetime.now()
496 1
            raw_url = "http://api.dryg.net/dagar/v2.1/{year}/{month}/{day}"
497 1
            url = raw_url.format(year=now.year, month=now.month, day=now.day)
498 1
            r = requests.get(url, timeout=5)
499 1
            nameday_data = r.json()
500 1
            names = nameday_data["dagar"][0]["namnsdag"]
501 1
            if names:
502 1
                msg = getString("nameday", "somebody").format(",".join(names))
503
            else:
504 1
                msg = getString("nameday", "nobody")
505 1
        except Exception:
506 1
            msg = getString("nameday", "error")
507 1
    return msg
508
509 1
def marvinUptime(row):
510
    """
511
    Display info about uptime tournament
512
    """
513 1
    msg = None
514 1
    if "uptime" in row:
515 1
        msg = getString("uptime", "info")
516 1
    return msg
517
518 1
def marvinStream(row):
519
    """
520
    Display info about stream
521
    """
522 1
    msg = None
523 1
    if any(r in row for r in ["stream", "streama", "ström", "strömma"]):
524 1
        msg = getString("stream", "info")
525 1
    return msg
526
527 1
def marvinPrinciple(row):
528
    """
529
    Display one selected software principle, or provide one as random
530
    """
531 1
    msg = None
532 1
    if any(r in row for r in ["principle", "princip", "principer"]):
533 1
        principles = getString("principle")
534 1
        principleKeys = list(principles.keys())
535 1
        matchedKeys = [k for k in row if k in principleKeys]
536 1
        if matchedKeys:
537 1
            msg = principles[matchedKeys.pop()]
538
        else:
539 1
            msg = principles[random.choice(principleKeys)]
540 1
    return msg
541
542 1
def getJoke():
543
    """
544
    Retrieves joke from api.chucknorris.io/jokes/random?category=dev
545
    """
546 1
    try:
547 1
        url = getString("joke", "url")
548 1
        r = requests.get(url, timeout=5)
549 1
        joke_data = r.json()
550 1
        return joke_data["value"]
551 1
    except Exception:
552 1
        return getString("joke", "error")
553
554 1
def marvinJoke(row):
555
    """
556
    Display a random Chuck Norris joke
557
    """
558 1
    msg = None
559 1
    if any(r in row for r in ["joke", "skämt", "chuck norris", "chuck", "norris"]):
560 1
        msg = getJoke()
561 1
    return msg
562
563 1
def getCommit():
564
    """
565
    Retrieves random commit message from whatthecommit.com/index.html
566
    """
567 1
    try:
568 1
        url = getString("commit", "url")
569 1
        r = requests.get(url, timeout=5)
570 1
        res = r.text.strip()
571 1
        msg = "Använd detta meddelandet: '{}'".format(res)
572 1
        return msg
573 1
    except Exception:
574 1
        return getString("commit", "error")
575
576 1
def marvinCommit(row):
577
    """
578
    Display a random commit message
579
    """
580 1
    msg = None
581 1
    if any(r in row for r in ["commit", "-m"]):
582 1
        commitMsg = getCommit()
583
    return commitMsg
0 ignored issues
show
introduced by
The variable commitMsg does not seem to be defined in case any(ComprehensionArgNode) on line 581 is False. Are you sure this can never be the case?
Loading history...
584