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

marvin_actions.getCommit()   A

Complexity

Conditions 2

Size

Total Lines 11
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 8
nop 0
dl 0
loc 11
ccs 8
cts 8
cp 1
crap 2
rs 10
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 1
        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
        return res
572 1
    except Exception:
573 1
        return getString("commit", "error")
574
575 1
def marvinCommit(row):
576
    """
577
    Display a random commit message
578
    """
579 1
    msg = None
580 1
    if any(r in row for r in ["commit", "-m"]):
581 1
        commitMsg = getCommit()
582 1
        if commitMsg == "Du får komma på ett själv. Jag är trasig för tillfället!":
583 1
            return commitMsg
584 1
        msg = "Använd detta meddelandet: '{}'".format(commitMsg)
585
    return msg
586