diff --git a/color.py b/color.py new file mode 100644 index 0000000..50bd40a --- /dev/null +++ b/color.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 + +special = { + 'heart': '\u2764', + 'zwsp': '\u200b', + } + +colors = {name: code for code, *names in map(str.split, ''' +00 white w +01 black b +02 blue navy navyblue +03 green +04 red 05 brown maroon +06 purple +07 orange olive +08 yellow +09 lightgreen lime +10 teal greenbluecyan +11 lightcyan cyan aqua +12 lightblue royalblue royal +13 pink lightpurple fuchsia +14 grey gray +15 lightgrey lightgray silver +99 default +'''.strip().split('\n')) for name in names} + +from sys import argv +chan = argv[1] +output = '' +for arg in argv[2:]: + try: + if arg[0] == '-': + arg = arg[1:] + if arg[0] == '-': + output += arg + elif arg in special: + output += special[arg] + elif arg[0] == 'x': + output += chr(int(arg[1:])) + elif ',' in arg: + a, b = arg.split(',') + output += '\x03' + colors[a] + ',' + colors[b] + else: + output += '\x03' + colors[arg] + else: + output += arg + except: + continue + +with open('/home/zgrep/offtopiabday/irc.freenode.net/' + chan + '/in', 'w') as fh: + fh.write(output + '\n') diff --git a/comics.py b/comics.py new file mode 100644 index 0000000..48fdf3c --- /dev/null +++ b/comics.py @@ -0,0 +1,259 @@ +#!/usr/bin/env python3 + +from subprocess import Popen, PIPE +from urllib.request import urlopen, quote, Request +from json import loads as dejson +from re import compile as regex + +def cmd(args): + proc = Popen(args, stdout=PIPE) + while True: + line = proc.stdout.readline() + if line: + line = line[:-1] + yield str(line, 'utf-8', 'ignore') + else: + break + +# Imports are finished, as is the preparation for things later. +# Time to implement the searchers! + +def relevantxkcd(query): + try: + url = "https://relevantxkcd.appspot.com/process?action=xkcd&query=" + quote(query) + req = urlopen(url) + if req.code != 200: + return None + res = req.read().split() + sure = float(res[0]) + if sure < 0.07: + return None + num = int(res[2]) + return 'https://xkcd.com/' + str(num) + except: + return None + +def explainxkcd(query): + try: + url = "https://explainxkcd.com/wiki/api.php?action=query&list=search&srwhat=text&format=json&srsearch=" + quote(query) + url = Request(url, headers={'User-Agent': 'Mozilla'}) + req = urlopen(url) + if req.code != 200: + return None + res = dejson(req.read().decode()) + for item in res['query']['search']: + try: + num = int(item['title'].split(':')[0]) + return 'https://xkcd.com/' + str(num) + except: + pass + return None + except: + return None + +googleapi = 'AIzaSyDr1gkHH-18QheEJpdGwUMmhYYvtlIJ3bA' +googlecse = '017423361205507730360:p6-h8trjn5c' +googlereg = regex(r'xkcd\.com/(\d+)') + +def googlexkcd(query): + try: + url = 'https://www.googleapis.com/customsearch/v1?key=' + googleapi + '&cx=' + googlecse + '&q=' + quote(query) + req = urlopen(url) + if req.code != 200: + return None + print('| | Opened URL, status code 200.') + res = dejson(req.read().decode()) + print('| | Decoded JSON.') + for item in res['items']: + print('| | Checking:', item['link']) + match = googlereg.search(item['link']) + try: + num = int(match.group(1)) + print('| | Found a match:', num) + return 'https://xkcd.com/' + str(num) + except: + pass + return None + except: + return None + +tvtropesapi = 'AIzaSyCVAXiUzRYsML1Pv6RwSG1gunmMikTzQqY' +tvtropescse = '006443654034974345143:kc4pt9dnkle' +def googletvtropes(query): + try: + url = 'https://www.googleapis.com/customsearch/v1element?key=' + tvtropesapi + '&cx=' + tvtropescse + '&q=' + quote(query) + req = urlopen(url) + if req.code != 200: + return None + print('| | Opened URL, status code 200.') + res = dejson(req.read().decode()) + print('| | Decoded JSON.') + if 'results' in res.keys() and res['results']: + return(res['results'][0]['url']) + print('| | No results.') + return None + except: + return None + +def numberxkcd(segment): + query = '' + original = segment + while segment and segment[0].isdigit(): + query += segment[0] + segment = segment[1:] + if query: + return 'https://xkcd.com/' + query, '', segment + return '', '', original + +# Righty, so all our searching mechanisms are above us. Let's table 'em up in a dict. + +methods = { + 'xkcd': (relevantxkcd, googlexkcd, explainxkcd), +# 'smbc': (), +# 'satw': (), +# 'ssss': (), + 'tvtropes': (googletvtropes,), + } + +# They return answer, query, segment +special = { + 'xkcd': numberxkcd, + } + +# Now for matching quotes. + +matching = [ + ('"', '"'), + ("'", "'"), + ('“', '”'), + ('„', '”', '“'), + ('<', '>'), + ('«', '»'), + ('»', '«'), + ('‹', '›'), + ('《', '》'), + ('〈', '〉'), + ('「', '」'), + ('﹁', '﹂'), + ('『', '』'), + ('﹃', '﹄'), + ('(', ')'), + ('[', ']'), + ('{', '}'), + ('【', '】'), + ('〔', '〕'), + ('⦗', '⦘'), + ('〖', '〗'), + ('〘', '〙'), + ('‚', '’', '‘'), + ('lu', "li'u") + ] + + +# And for an attempt to extract a comic. + +def attempt(line): + output = [] + segment = line + + while segment: + i = len(segment) + c = '' + for comic in methods.keys(): + try: + n = segment.index(comic + '!') + if n < i: + i = n + c = comic + except: + pass + + if not c: + # We have not found any comic-related things in this line. Bye! + return None + print('Searching for ' + c + ':') + + i += len(c) + 1 # len(comic + '!') + segment = segment[i:] # Right, skippity skip. + query = '' + + # Special cases. + if c in special.keys(): + print("| There's a special case:") + result, query, segment = special[c](segment) + if result: + print("| | Result:", result) + output.append(result) + continue + if not query: + print("| | Special case did not match.") + + if not query: + # See if there's a quoted thing. + for pair in matching: + start, *ends = pair + l = len(start) + if segment[:l] != start: + continue + print('| Found matching initial quote:', start) + segment = segment[l:] + for end in ends: + try: + i = segment.index(end) + query = segment[:i] + segment = segment[i + len(end):] + print('| Found matching end:', end) + break + except: + pass + if query: + break + + if not query: + while segment: + if not segment[0].isspace(): + query += segment[0] + segment = segment[1:] + else: + break + if '_' in query: + print('| Replacing "_" with " ".') + query = query.replace('_', ' ') + elif '-' in query: + print('| Replacing "-" with " ".') + query = query.replace('-', ' ') + + if query: + query = query.strip() + print('| Searching for:', query) + for method in methods[c]: + print('| Search using:', method.__name__) + result = method(query) + if result is not None: + output.append(result) + print('| Result:', result) + break + + return ' '.join(output) + +# Connect it to happybot. + +from sys import argv + +if len(argv) != 3: + print('Usage: ./xkcd.py out in') + exit(1) + +for line in cmd(['tail', '-f', argv[1]]): + line = line.split(' ', 3)[3] + # ACTIONs shmacktions, I don't give a char 'bout that. + if line[:8] == '\x01ACTION ' and line[-1] == '\x01': + line = line[8:-1] + # Oh, oh noes! We can't look at these lines! HUMANS ARE EVIL! + if line.startswith('\u200b') or line.startswith('nolog:') or line.startswith('[nolog]'): + continue + # I'm trying... trying... + result = attempt(line) + if result: + with open(argv[2], 'w') as fh: + fh.write('\u200b' + result + '\n') diff --git a/happy b/happy new file mode 100644 index 0000000..96dd257 --- /dev/null +++ b/happy @@ -0,0 +1,3 @@ +#!/usr/bin/env sh + +echo -e "$(cat /home/zgrep/zwsp)\x02\x0311,01Happy $@! ♥" >> "/home/zgrep/offtopiabday/irc.freenode.net/#offtopia/in"; diff --git a/happybot/ichi.py b/happybot/ichi.py deleted file mode 100644 index 5f6bdb1..0000000 --- a/happybot/ichi.py +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/env python3 - -import requests as rq -from lxml import html - -from sys import argv -if len(argv) != 2: - print('Not enough arguments. Bug zgrep.') - exit(1) - -q = argv[1] - -r = rq.get('http://ichi.moe/cl/qr/', params={'q': q}) - -if r.status_code != 200: - print('Non-200 status code. Bug zgrep.') - exit() - -# tree -t = html.fromstring(r.content) - -ichi = ''.join(t.xpath('//span[@class="ds-text"]//text()')) - -print(ichi) diff --git a/happybot/ichi.sh b/happybot/ichi.sh deleted file mode 100644 index 4c2575d..0000000 --- a/happybot/ichi.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env ash - -. /home/zgrep/offtopiabday/happybot/common.sh - -irc | while read -r n m; do - if reg '^(happy|hate)bot[:,] ichi (.*)' "$m"; then - python3 happybot/ichi.py "$(m 2)" | say; - fi; -done; diff --git a/happys b/happys new file mode 100644 index 0000000..9f2277e --- /dev/null +++ b/happys @@ -0,0 +1,3 @@ +#!/usr/bin/env sh + +echo -e "$(cat /home/zgrep/zwsp)\x02\x0311,01Happy$@! ♥" >> "/home/zgrep/offtopiabday/irc.freenode.net/#offtopia/in"; diff --git a/hateweekend b/hateweekend new file mode 100644 index 0000000..fb49c35 --- /dev/null +++ b/hateweekend @@ -0,0 +1,5 @@ +#!/usr/bin/env sh + +rm /home/zgrep/offtopiabday/hateweekfile; +echo -e "$(cat /home/zgrep/zwsp)\x02\x0304,01I hate how hateweek is over!" >> "/home/zgrep/offtopiabday/irc.freenode.net/#offtopia/in"; +echo "/n happybot" >> "/home/zgrep/offtopiabday/irc.freenode.net/in"; diff --git a/hateweekstart b/hateweekstart new file mode 100644 index 0000000..69709ea --- /dev/null +++ b/hateweekstart @@ -0,0 +1,5 @@ +#!/usr/bin/env sh + +touch /home/zgrep/offtopiabday/hateweekfile; +echo "/n hatebot" >> "/home/zgrep/offtopiabday/irc.freenode.net/in"; +echo -e "$(cat /home/zgrep/zwsp)\x02\x0304,01Hateweek is here! Hate how it's only once a year!" >> "/home/zgrep/offtopiabday/irc.freenode.net/#offtopia/in"; diff --git a/hhcdlog.sh b/hhcdlog.sh new file mode 100644 index 0000000..24e1f2b --- /dev/null +++ b/hhcdlog.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +happy=':-*D|=D|(:|։|‎܃‎|‎܄‎|᛬|︰|᠃|᠉|⁚|‎׃‎|˸|꞉|∶|ː|ꓽ|⠨|⡁|⠅)(ᴅ|Ⅾ|ⅅ|𝐃|𝐷|𝑫|𝒟|𝓓|𝔇|𝔻|𝕯|𝖣|𝗗|𝘋|𝘿|𝙳|Ꭰ|ᗞ|ᗪ|ꓓ|ᴰ|Ɖ|Ð|⫐|𐌃|Ɒ)' +tail -n 200 "/home/zgrep/offtopiabday/irc.freenode.net/##:d/out" | awk '{if($3=="-!-")gsub(/\([^)]+\)/,"");if(/^\S+ \S+ \S+ :D$/){}else if(/'"$happy"'/){sub(/>.*/, "> [REDACTED: NON-CONFORMANT]")}else{sub(/>.*/, "> [REDACTED: NON-HAPPY]")};print}' diff --git a/ichi.py b/ichi.py new file mode 100644 index 0000000..c70b58b --- /dev/null +++ b/ichi.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 + +from sys import argv +import re +import requests as rq +from lxml import html +from subprocess import Popen, PIPE + +if len(argv) != 2: + print('Usage:', argv[0], '#channel') + exit(1) +chan = argv[1] + +def cmd(args): + proc = Popen(args, stdout=PIPE) + while True: + line = proc.stdout.readline() + if line: + try: + yield str(line[:-1], 'utf-8', 'ignore') + except: + pass + else: + break + +fdir = '/home/zgrep/offtopiabday/irc.freenode.net/' + chan +fin = fdir + '/in' +fout = fdir + '/out' + +def irc(): + for line in cmd(['tail', '-n', '0', '-f', fout]): + date, time, nick, line = line.split(' ', 3) + print(line) + m = re.match(r'(?i)((happy|hate)bot[:,]\s+ichi|ハピロボット[:,])\s(.+)', line) + if m: + q = m.group(3) + print('Matched!', q) + r = get(q) + print('Result!', r) + with open(fin, 'w') as fh: + fh.write(r + '\n') + +omitnum = 3 + +def ichi(q, extra=False): + r = rq.get('http://ichi.moe/cl/qr/', params={'q': q}) + if r.status_code != 200: + return 'Non-200 status code. Bug zgrep.' + t = html.fromstring(r.content) + ichi = ''.join(t.xpath('//span[@class="ds-text"]//text()')) + more = '' + if extra: + e = t.xpath('//span[@class="gloss-desc"]//text()') + more = ' - '.join(e[:omitnum]) + if len(e) > omitnum: + more += ' - \x1dmore omitted\x0f' + if more: + more = ' (' + more + ')' + return '"' + ichi + '"' + more + +def kanji(q): + r = rq.get('http://ichi.moe/cl/kanji/', params={'q': q}) + if r.status_code != 200: + return None + if 'No kanji found' in r.text: + return None + t = html.fromstring(r.content) + return 'Not yet implemented.' + +def get(q): + if len(q) == 1: +# k = kanji(q) +# if k: return k + return ichi(q, True) + return ichi(q) + +irc() diff --git a/iiundie b/iiundie new file mode 100644 index 0000000..d9eac02 --- /dev/null +++ b/iiundie @@ -0,0 +1 @@ +$(cat starter | sed '/load_modules/q') diff --git a/privmsg b/privmsg new file mode 100644 index 0000000..8a86aaa --- /dev/null +++ b/privmsg @@ -0,0 +1,5 @@ +#!/usr/bin/env sh + +nick="$1"; shift; + +echo "/privmsg $nick :$@" >> "/home/zgrep/offtopiabday/irc.freenode.net/in"; diff --git a/rfk/rfk.py b/rfk/rfk.py new file mode 100644 index 0000000..9d72a1f --- /dev/null +++ b/rfk/rfk.py @@ -0,0 +1,313 @@ +#!/usr/bin/env python3 + +from subprocess import Popen, PIPE +from random import choice, random +from time import sleep +from os import chdir + +map_url = 'https://zgrep.org/taxreturns/rfk.txt' + +# Log. + +def logging(msg): + with open('log.txt', 'a') as fh: + fh.write(msg.strip() + '\n') + +# Items. + +def get_items(): + with open('rfk.txt') as fh: + return [l.strip() for l in fh] + +def add_item(item): + items = set(get_items() + [item]) + with open('rfk.txt', 'w') as fh: + fh.write('\n'.join(items) + '\n') + logging('add item: ' + item) + return 'Added item: ' + item + +def del_item(item): + items = set(get_items()) + if item in items: + items.remove(item) + with open('rfk.txt', 'w') as fh: + fh.write('\n'.join(items) + '\n') + logging('del item: ' + item) + return 'Removed item: ' + item + else: + removal = None + for maybe in items: + if maybe.lower().startswith(item.lower()): + if removal: + break + else: + removal = maybe + else: + if removal: + items.remove(removal) + with open('rfk.txt', 'w') as fh: + fh.write('\n'.join(items) + '\n') + logging('del item: ' + removal) + return 'Removed item: ' + removal + else: + return 'No items matched: ' + item + return 'More than one item matched: ' + item + +# Robot finds kitten bits below. + +grid = {} +width, height = 30, 15 +rx, ry = width // 2, height // 2 +percent = 0.1 +blind = False +found = False + +def string(): # prints the grid as a string + global grid, rx, ry, width, height, found + result = '' + for y in range(height): + for x in range(width): + if (x, y) in grid: + item = grid[(x, y)] + if found and item[2]: + result += 'K' + else: + result += item[0] + elif x == rx and y == ry: + result += '#' + else: + result += ' ' + result += '\n' + return result + +def move(x, y): # teleports to location + global rx, ry, width, height, grid, found + if x < 0 or y < 0 or x >= width or y >= height: + return 'There is a wall in the way.' + elif (x, y) in grid: + result = grid[(x, y)] + if result[2]: + found = True + return result[1] + else: + rx, ry = x, y + return None + +up = lambda: move(rx, ry - 1) +down = lambda: move(rx, ry + 1) +left = lambda: move(rx - 1, ry) +right = lambda: move(rx + 1, ry) + +symbols = '~!@$%^&*()`[]{}=+\\/?|-_;:\'"<>,.abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ' + +def reset(): # Set up a new game. + global width, height, rx, ry, grid, percent, found + grid = {} + found = False + items = get_items() + rx, ry = width // 2, height // 2 + for y in range(height): + for x in range(width): + if random() <= percent and x != rx and y != ry: + grid[(x, y)] = (choice(symbols), choice(items), False) + if not grid: + kitten = (rx - 2, ry - 2) + else: + kitten = choice(list(grid)) + with open('found.txt') as fh: + messages = [f.strip('\n') for f in fh.read().strip('\n').split('\n%\n')] + grid[kitten] = (choice(symbols), choice(messages), True) + if len(grid) == 1: + objects = 'there is 1 object' + else: + objects = 'there are {} objects'.format(len(grid)) + if not blind: + append = 'Map: ' + map_url + else: + append = 'No map is being made, blind is set to true.' + return 'New game! The grid is {} by {}, and {}. You are in the center. {}'\ + .format(width, height, objects, append) + +# Parsing input. + +def tofile(): + global blind + if not blind: + with open('/home/zgrep/tax/rfk.txt', 'w') as fh: + fh.write(string()) + +def dedup(gen): + prev = None + for item in gen: + if item == prev: + continue + else: + prev = item + yield item + +def parse(cmd, normalcase): + global width, height, percent, blind + if all(c in 'udlrnsew^v<>←↑→↓' for c in cmd): + for c in cmd: + if c in 'u^n↑': + yield up() + elif c in 'dvs→': + yield down() + elif c in 'le↓': + yield right() + tofile() + elif cmd in ('up', 'north'): + yield up() + tofile() + elif cmd in ('down', 'south'): + yield down() + tofile() + elif cmd in ('left', 'west'): + yield left() + tofile() + elif cmd in ('right', 'east'): + yield right() + tofile() + elif cmd.startswith('size '): + cmd = cmd.replace('size ', '', 1) + try: + w, h = map(int, cmd.split('x')) + w = min(max(w, 5), 1000) + h = min(max(h, 5), 1000) + width, height = w, h + yield reset() + tofile() + yield topic() + except: + yield 'The correct format for size is 10x10.' + elif cmd.startswith('blind '): + cmd = cmd.replace('blind ', '', 1) + if ' ' not in cmd: + if cmd in ('true', 'yes', 'on', '1'): + blind = True + with open('/home/zgrep/tax/rfk.txt', 'w') as fh: + fh.write('Blind is turned on, no map is being generated.\n') + yield topic() + yield 'Blind has been set to true, no new maps with be generated.' + elif cmd in ('false', 'no', 'off', '0'): + blind = False + tofile() + yield topic() + yield 'Blind has been turned off, a map has been generated: ' + map_url + else: + yield 'Expected either true or false.' + elif cmd.startswith('prob '): + cmd = cmd.replace('prob ', '', 1) + try: + m = 1 + if cmd[-1] == '%': + cmd = cmd[:-1] + m = 0.01 + p = min(max(m*float(cmd), 0), 1) + percent = p + yield reset() + tofile() + yield topic() + except: + yield 'The correct format for prob is 0.1 or 10%.' + elif cmd.startswith('add item '): + yield add_item(normalcase[9:]) + elif cmd.startswith('del item '): + yield del_item(normalcase[9:]) + elif cmd in ('reset', 'new game', 'restart'): + yield reset() + tofile() + yield topic() + elif cmd == 'help': + yield 'Commands: size, prob, add item, del item, up (north), down (south), left (west), right (east), new game, blind, map, help.' + yield '^<>vudlrnsew becomes up left right down up down left right north south east west. Only the one-character short commands can be chained together in one IRC message.' + if not blind: + yield 'A map of the grid can be found at: {}'.format(map_url) + else: + yield 'Currently blind is set to true, and therefore no map of the grid is generated. But normally, it would be found here: {}'.format(map_url) + elif cmd in ('map', 'grid'): + yield map_url + elif cmd == 'size': + yield '{} by {}'.format(width, height) + elif cmd == 'prob': + s = 's' + if len(grid) == 1: s = '' + yield '{}%, with {} object{} on the current grid.'\ + .format(percent*100, len(grid), s) + elif cmd == 'blind': + yield ('blind is set to false, a map is being generated.', + 'blind is set to true, no map is being generated.')[blind] + +# IRC-specific bits below. + +def topic(): + global found, width, height, grid + s = 's' + if len(grid) == 1: s = '' + append = 'No map.' + if not blind: + append = 'Map: ' + map_url + finding = '' + if found is True: + finding = ' Kitten has been found!' + elif found: + finding = ' Kitten has been found by {}!'.format(found) + return '/t Robot finds kitten. To play, type "help". | The grid is {} by {}, and has {} object{}.{} {}'\ + .format(width, height, len(grid), s, finding, append) + +def cmd(args): + proc = Popen(args, stdout=PIPE) + while True: + line = proc.stdout.readline() + if line: + try: + yield str(line[:-1], 'utf-8', 'ignore') + except: + pass + else: + break + +def begin(fin, fout): + global found + + fin = '/home/zgrep/offtopiabday/' + fin + fout = '/home/zgrep/offtopiabday/' + fout + + def send(msg): + if msg: + with open(fin, 'w') as fh: + fh.write(msg.strip() + '\n') + + send(reset()) + tofile() + send(topic()) + + for line in cmd(['tail', '-n', '0', '-f', fout]): + _, _, nick, line = line.split(' ', 3) + nick = nick[1:-1] + if nick in ('happybot', 'hatebot'): + continue + output = list(dedup(parse(line.lower(), line))) + if found is True: + found = nick + with open('finders.txt', 'a') as fh: + fh.write(found + '\n') + send(topic()) + time = 0 + if len(output) > 3: + time = 0.5 + for line in output: + send(line) + sleep(time) + +from sys import argv + +if len(argv) != 3: + print('Usage: in out') + exit(1) + +chdir('/home/zgrep/offtopiabday/rfk') + +begin(*argv[1:]) diff --git a/urls.py b/urls.py new file mode 100644 index 0000000..1a69df4 --- /dev/null +++ b/urls.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 + +def sizeof_fmt(num, suffix='B'): + for unit in ['','Ki','Mi','Gi','Ti','Pi','Ei','Zi']: + if abs(num) < 1024.0: + return "%3.1f%s%s" % (num, unit, suffix) + num /= 1024.0 + return "%.1f%s%s" % (num, 'Yi', suffix) + +from re import compile as regex + +urls = regex(r'(?:(?:https?|ftp)://)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,}))\.?)(?::\d{2,5})?(?:[/?#]\S*)?') + +from subprocess import Popen, PIPE + +def cmd(*args): + proc = Popen(args, stdout=PIPE) + while True: + line = proc.stdout.readline() + if line: + try: + yield str(line[:-1], 'utf-8', 'ignore') + except: + pass + else: + break + +from sys import argv + +if len(argv) != 2: + print('Usage:', argv[0], '#channel') + exit(1) + +chan = '/home/zgrep/offtopiabday/irc.freenode.net/' + argv[1] + +from urllib.request import Request, urlopen + +def quote(url): + res = '' + for c in url: + if ord(c) > 127: + res += ''.join('%' + hex(b)[2:] for b in c.encode('utf-8')) + else: + res += c + return res + +irccloud_none = 'irccloud.com/pastebin/' +irccloud_with = irccloud_none + 'raw/' + +for line in cmd('tail', '-n', '0', '-f', chan + '/out'): + date, time, nick, line = line.split(' ', 3) + nick = nick[1:-1] + if nick in ('happybot', 'hatebot'): + continue + result = [] + print('Doing line:', line) + for url in urls.findall(line): + url = quote(url) + if irccloud_none in url and irccloud_with not in url: + result.append(url.replace(irccloud_none, irccloud_with, 1)) + continue + if url[-5:] == '.gifv': # hack for imgur gifv's + url = url[:-5] + print('| Got gifv:', url) + try: + r = urlopen(Request(url + '.mp4', method='HEAD')) + contenttype = r.getheader('content-type') + length1 = r.getheader('content-length') + r.close() + except: + print('| Could not get mp4.') + if 'video' not in contenttype.lower(): + print('| Video is not a video?') + result.append('???') + continue + try: + r = urlopen(Request(url + '.gif', method='HEAD')) + contenttype = r.getheader('content-type') + length2 = r.getheader('content-length') + r.close() + except: + print('| Could not get gif.') + if 'image' not in contenttype.lower(): + print('| Image is not an image?') + result.append('???') + continue + try: + length1 = int(length1) + length2 = int(length2) + except: + print('| Lengths are not ints.') + continue + if length1 <= length2: + url += '.mp4' + length = length1 + else: + url += '.gif' + length = length2 + result.append(url + ' ' + sizeof_fmt(length)) + continue + print('| Got URL:', url) + rq = Request(url, method='HEAD') + try: + r = urlopen(rq) + contenttype = r.getheader('content-type').lower() + length = r.getheader('content-length') + r.close() + print('| | HEAD request completed.') + download = 0 # 0 ignore, 1 get from HEAD, 2+ get from HEAD otherwise GET + if 'image' in contenttype: + download = 2 # download images, fine... + elif 'video' in contenttype: + download = 1 # Eh... I'll draw the line at videos. + if download > 0: + if length: + try: + b = int(length) + except: + b = -1 + elif download > 1: + try: + rq = Request(url, method='HEAD') + r = urlopen(rq) + b = len(r.read()) + r.close() + print('| | Normal request required and complete.') + except: + b = -1 + print('| | Normal request required and failed.') + if b < 0: + print('| | Failure.') + result.append('???') + else: + print('| | Success.') + result.append(sizeof_fmt(b)) + except: + print('| | Failure.') + if result: + with open(chan + '/in', 'w') as fh: + fh.write('[' + '] ['.join(result) + ']\n')