Passed
Push — master ( 4c1a37...790f62 )
by Mikael
03:03
created

marvin_actions.marvinSun()   A

Complexity

Conditions 3

Size

Total Lines 23
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 3.0021

Importance

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