summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/fingerchange.py153
1 files changed, 153 insertions, 0 deletions
diff --git a/src/fingerchange.py b/src/fingerchange.py
new file mode 100755
index 0000000..b34c009
--- /dev/null
+++ b/src/fingerchange.py
@@ -0,0 +1,153 @@
+#!/usr/bin/env python3
+
+from textwrap import fill, indent
+from bs4 import BeautifulSoup
+import requests
+import asyncio
+import json
+
+api_url = 'https://api.stackexchange.com/2.2/users/'
+
+
+def slice_dict(dict, slice):
+ s_len = len(slice)
+ return {key[s_len:]: value for key,value in dict.items()
+ if key.startswith(slice)}
+
+
+def get_user_by_uid(site, uid):
+ params = {
+ 'site': site,
+ 'filter': '!9YdnSA07B',
+ }
+ res = requests.get(api_url + str(uid), params=params)
+ res = json.loads(res.text)
+
+ return res['items'][0]
+
+
+def get_users_by_name(site, name=''):
+ params = {
+ 'site': site,
+ 'inname': name,
+ 'filter': '!9YdnSA07B',
+ }
+ res = requests.get(api_url, params=params)
+ res = json.loads(res.text)
+
+ return res['items']
+
+
+def format_url(url):
+ return '<{}>'.format(url)
+
+
+def format_field(key, value=None, indent=1):
+ if not value:
+ return '{}:: {}\n'.format(' '*indent, key)
+
+ return '{}:: {} {}\n'.format(' '*indent, key, value)
+
+
+def format_header(header, filler):
+ header = str(header)
+ header_len = len(header)
+ filler_len = 80 - header_len - (3 + 2)
+
+ msg = '{pre_fil} {header} {post_fil}\n'.format(**{
+ 'pre_fil': filler * 3,
+ 'header': header,
+ 'post_fil': filler * filler_len,
+ })
+ return msg
+
+
+def format_rep_changes(user):
+ changes = slice_dict(user, 'reputation_')
+ msg = ''
+ if changes['change_day'] > 0:
+ msg += format_field('day', '+' + str(changes['change_day']), indent=3)
+ if changes['change_quarter'] > 0:
+ msg += format_field('quarter', '+' + str(changes['change_quarter']), indent=3)
+ if changes['change_year'] > 0:
+ msg += format_field('year', '+' + str(changes['change_year']), indent=3)
+
+ if len(msg) > 0:
+ msg = format_field('reputation changes') + msg
+
+ return msg
+
+
+def format_user(user, long_output=True):
+ msg = '\n'
+ msg += format_header(user['account_id'], '=')
+ msg += format_field('uid', user['user_id'])
+ msg += format_field('name', user['display_name'])
+ msg += format_field('reputation', user['reputation'])
+ if 'location' in user:
+ msg += format_field('location', user['location'])
+
+ msg += format_field('profile', format_url(user['link']))
+ if 'website_url' in user and len(user['website_url']) > 0:
+ msg += format_field('website', format_url(user['website_url']))
+
+ msg += format_rep_changes(user)
+ msg += '\n'
+
+ if long_output and 'about_me' in user and len(user['about_me']) > 0:
+ msg += format_header('About User', '-') + '\n'
+
+ soup = BeautifulSoup(user['about_me'])
+ text = soup.get_text()
+
+ lines = [ line for line in text.splitlines() if line.strip() ]
+ for line in lines:
+ msg += indent(fill(line), ' ') + '\n\n'
+
+ return msg
+
+
+@asyncio.coroutine
+def handle_client(reader, writer):
+ line = yield from reader.readline()
+ line = line.decode('utf-8').strip()
+
+ if '@' not in line:
+ writer.write('Error: Username or site missing\n'.encode('utf-8'))
+ writer.close()
+ return
+
+ user, site = line.split('@')
+ if user.startswith('/W'):
+ long_output = True
+ user = user[3:]
+ else:
+ long_output = False
+
+ try:
+ uid = int(user)
+ users = [get_user_by_uid(site, uid)]
+ except ValueError:
+ users = get_users_by_name(site, user)
+
+ for user in users:
+ if user['user_type'] == 'unregistered':
+ continue
+
+ msg = format_user(user, long_output)
+ writer.write(msg.encode('utf-8'))
+
+ writer.close()
+
+
+loop = asyncio.get_event_loop()
+
+coro = asyncio.start_server(handle_client, host='0.0.0.0', port=79, loop=loop)
+server = loop.run_until_complete(coro)
+
+try:
+ loop.run_forever()
+except KeyboardInterrupt:
+ server.close()
+ loop.run_until_complete(server.wait_closed())
+ loop.close()