A fresh start
This commit is contained in:
parent
d27991269a
commit
68380bd5f2
|
|
@ -1,11 +0,0 @@
|
||||||
from slackclient import SlackClient
|
|
||||||
|
|
||||||
|
|
||||||
#One last call from beyond the grave
|
|
||||||
apifile = open("apitoken.txt", 'r')
|
|
||||||
SLACK_API = next(apifile).strip()
|
|
||||||
apifile.close();
|
|
||||||
|
|
||||||
slack = SlackClient(SLACK_API)
|
|
||||||
slack.api_call("chat.postMessage", channel="@jacobhenry", text="one error. and im die?")
|
|
||||||
|
|
||||||
140
GoogleApi.py
140
GoogleApi.py
|
|
@ -1,140 +0,0 @@
|
||||||
"""
|
|
||||||
Examples provided by google for using their api.
|
|
||||||
Very slightly modified by me to easily just get credentials
|
|
||||||
"""
|
|
||||||
import httplib2
|
|
||||||
import os
|
|
||||||
|
|
||||||
from apiclient import discovery
|
|
||||||
import oauth2client
|
|
||||||
from oauth2client import client
|
|
||||||
from oauth2client import tools
|
|
||||||
|
|
||||||
try:
|
|
||||||
import argparse
|
|
||||||
flags = argparse.ArgumentParser(parents=[tools.argparser]).parse_args(["--noauth_local_webserver"])
|
|
||||||
except ImportError:
|
|
||||||
flags = None
|
|
||||||
|
|
||||||
# If modifying these scopes, delete your previously saved credentials
|
|
||||||
# at ~/.credentials/sheets.googleapis.com-python-quickstart.json
|
|
||||||
SCOPES = 'https://www.googleapis.com/auth/spreadsheets.readonly'
|
|
||||||
CLIENT_SECRET_FILE = 'client_secret.json'
|
|
||||||
APPLICATION_NAME = 'SlickSlacker'
|
|
||||||
|
|
||||||
def get_sheets_service(credentials):
|
|
||||||
http = credentials.authorize(httplib2.Http())
|
|
||||||
discoveryUrl = ('https://sheets.googleapis.com/$discovery/rest?'
|
|
||||||
'version=v4')
|
|
||||||
service = discovery.build('sheets', 'v4', http=http, discoveryServiceUrl=discoveryUrl)
|
|
||||||
|
|
||||||
return service
|
|
||||||
|
|
||||||
def get_sheets_credentials():
|
|
||||||
"""Gets valid user credentials from storage.
|
|
||||||
|
|
||||||
If nothing has been stored, or if the stored credentials are invalid,
|
|
||||||
the OAuth2 flow is completed to obtain the new credentials.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Credentials, the obtained credential.
|
|
||||||
"""
|
|
||||||
home_dir = os.path.expanduser('~')
|
|
||||||
credential_dir = os.path.join(home_dir, '.credentials')
|
|
||||||
if not os.path.exists(credential_dir):
|
|
||||||
os.makedirs(credential_dir)
|
|
||||||
credential_path = os.path.join(credential_dir, 'sheets.googleapis.com-python-quickstart.json')
|
|
||||||
|
|
||||||
store = oauth2client.file.Storage(credential_path)
|
|
||||||
credentials = store.get()
|
|
||||||
if not credentials or credentials.invalid:
|
|
||||||
flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
|
|
||||||
flow.user_agent = APPLICATION_NAME
|
|
||||||
if flags:
|
|
||||||
credentials = tools.run_flow(flow, store, flags)
|
|
||||||
else: # Needed only for compatibility with Python 2.6
|
|
||||||
credentials = tools.run(flow, store)
|
|
||||||
print('Storing credentials to ' + credential_path)
|
|
||||||
return credentials
|
|
||||||
|
|
||||||
def get_calendar_credentials():
|
|
||||||
"""Gets valid user credentials from storage.
|
|
||||||
|
|
||||||
If nothing has been stored, or if the stored credentials are invalid,
|
|
||||||
the OAuth2 flow is completed to obtain the new credentials.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Credentials, the obtained credential.
|
|
||||||
"""
|
|
||||||
home_dir = os.path.expanduser('~')
|
|
||||||
credential_dir = os.path.join(home_dir, '.credentials')
|
|
||||||
if not os.path.exists(credential_dir):
|
|
||||||
os.makedirs(credential_dir)
|
|
||||||
credential_path = os.path.join(credential_dir, 'calendar-python-quickstart.json')
|
|
||||||
|
|
||||||
store = oauth2client.file.Storage(credential_path)
|
|
||||||
credentials = store.get()
|
|
||||||
if not credentials or credentials.invalid:
|
|
||||||
flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
|
|
||||||
flow.user_agent = APPLICATION_NAME
|
|
||||||
if flags:
|
|
||||||
credentials = tools.run_flow(flow, store, flags)
|
|
||||||
else: # Needed only for compatibility with Python 2.6
|
|
||||||
credentials = tools.run(flow, store)
|
|
||||||
print('Storing credentials to ' + credential_path)
|
|
||||||
return credentials
|
|
||||||
|
|
||||||
def sheets_quickstart():
|
|
||||||
"""Shows basic usage of the Sheets API.
|
|
||||||
|
|
||||||
Creates a Sheets API service object and prints the names and majors of
|
|
||||||
students in a sample spreadsheet:
|
|
||||||
https://docs.google.com/spreadsheets/d/1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms/edit
|
|
||||||
"""
|
|
||||||
credentials = get_sheets_credentials()
|
|
||||||
http = credentials.authorize(httplib2.Http())
|
|
||||||
discoveryUrl = ('https://sheets.googleapis.com/$discovery/rest?'
|
|
||||||
'version=v4')
|
|
||||||
service = discovery.build('sheets', 'v4', http=http,
|
|
||||||
discoveryServiceUrl=discoveryUrl)
|
|
||||||
|
|
||||||
spreadsheetId = '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms'
|
|
||||||
rangeName = 'Class Data!A2:E'
|
|
||||||
result = service.spreadsheets().values().get(
|
|
||||||
spreadsheetId=spreadsheetId, range=rangeName).execute()
|
|
||||||
values = result.get('values', [])
|
|
||||||
|
|
||||||
if not values:
|
|
||||||
print('No data found.')
|
|
||||||
else:
|
|
||||||
print('Name, Major:')
|
|
||||||
for row in values:
|
|
||||||
# Print columns A and E, which correspond to indices 0 and 4.
|
|
||||||
print('%s, %s' % (row[0], row[4]))
|
|
||||||
|
|
||||||
def calendar_quickstart():
|
|
||||||
"""Shows basic usage of the Google Calendar API.
|
|
||||||
|
|
||||||
Creates a Google Calendar API service object and outputs a list of the next
|
|
||||||
10 events on the user's calendar.
|
|
||||||
"""
|
|
||||||
credentials = get_calendar_credentials()
|
|
||||||
http = credentials.authorize(httplib2.Http())
|
|
||||||
service = discovery.build('calendar', 'v3', http=http)
|
|
||||||
|
|
||||||
now = datetime.datetime.utcnow().isoformat() + 'Z' # 'Z' indicates UTC time
|
|
||||||
print('Getting the upcoming 10 events')
|
|
||||||
eventsResult = service.events().list(
|
|
||||||
calendarId='primary', timeMin=now, maxResults=10, singleEvents=True,
|
|
||||||
orderBy='startTime').execute()
|
|
||||||
events = eventsResult.get('items', [])
|
|
||||||
|
|
||||||
if not events:
|
|
||||||
print('No upcoming events found.')
|
|
||||||
for event in events:
|
|
||||||
start = event['start'].get('dateTime', event['start'].get('date'))
|
|
||||||
print(start, event['summary'])
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
||||||
|
|
@ -1,81 +0,0 @@
|
||||||
"""
|
|
||||||
This file contains util for scroll polling
|
|
||||||
Only really kept separate for neatness sake.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import re
|
|
||||||
from SlackUtil import reply
|
|
||||||
from fuzzywuzzy import fuzz
|
|
||||||
from fuzzywuzzy import process
|
|
||||||
|
|
||||||
|
|
||||||
#load the family tree
|
|
||||||
familyfile = open("sortedfamilytree.txt", 'r')
|
|
||||||
|
|
||||||
#Parse out
|
|
||||||
p = re.compile("([0-9]*): (.*)$")
|
|
||||||
brothers = [p.match(line) for line in familyfile]
|
|
||||||
brothers = [m for m in brothers if m]
|
|
||||||
brothers = [{
|
|
||||||
"scroll": int(m.group(1)),
|
|
||||||
"name": m.group(2)
|
|
||||||
} for m in brothers]
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
Attempts to look up a user by scroll
|
|
||||||
"""
|
|
||||||
def handleScrollMsg(slack, msg):
|
|
||||||
#Initialize response
|
|
||||||
response = None
|
|
||||||
|
|
||||||
#Get text
|
|
||||||
text = msg['text']
|
|
||||||
|
|
||||||
p = re.compile("scroll\\s*(.*?)$")
|
|
||||||
m = p.match(text)
|
|
||||||
if not m:
|
|
||||||
response = "Could not parse your query. Please invoke as \"scroll <name>\" or \"scroll <scroll#>\""
|
|
||||||
else:
|
|
||||||
response = getResponseByQuery(m.group(1))
|
|
||||||
|
|
||||||
reply(slack, msg, response, username="scrollbot")
|
|
||||||
|
|
||||||
def getResponseByQuery(query):
|
|
||||||
try:
|
|
||||||
#Try get scroll number
|
|
||||||
scroll = int(query)
|
|
||||||
b = findBrotherByScroll(scroll)
|
|
||||||
|
|
||||||
if b:
|
|
||||||
return "Brother {0} has scroll {1}".format(b["name"], b["scroll"])
|
|
||||||
else:
|
|
||||||
return "Could not find scroll {0}".format(scroll)
|
|
||||||
except ValueError:
|
|
||||||
#Use as name
|
|
||||||
name = query
|
|
||||||
b = findBrotherByName(name)
|
|
||||||
|
|
||||||
if b:
|
|
||||||
return "Best match:\nBrother {0} has scroll {1}".format(b["name"], b["scroll"])
|
|
||||||
else:
|
|
||||||
return "Could not find brother {0}".format(name)
|
|
||||||
|
|
||||||
|
|
||||||
def findBrotherByScroll(scroll):
|
|
||||||
for b in brothers:
|
|
||||||
if b["scroll"] == scroll:
|
|
||||||
return b
|
|
||||||
return None
|
|
||||||
|
|
||||||
def findBrotherByName(name):
|
|
||||||
#Try direct lookup
|
|
||||||
"""
|
|
||||||
for b in brothers:
|
|
||||||
if name.toLower() in b["name"].toLower():
|
|
||||||
return b
|
|
||||||
"""
|
|
||||||
|
|
||||||
#Do fuzzy match
|
|
||||||
return process.extractOne(name, brothers, processor=lambda b: b["name"])[0]
|
|
||||||
|
|
||||||
61
SheetUtil.py
61
SheetUtil.py
|
|
@ -1,61 +0,0 @@
|
||||||
"""
|
|
||||||
This file contains general utility for polling google sheets.
|
|
||||||
Note that it is not used for the REQUISITION of the service,
|
|
||||||
as that is in the domain of GoogleApi.py.
|
|
||||||
|
|
||||||
It is instead for more general things, like parsing a sheet into 2d array,
|
|
||||||
etc.
|
|
||||||
My goal is to abstract all of the sheets bullshit out, and make reading
|
|
||||||
from a sheet as easy as working with a 2d array, as well as making it
|
|
||||||
fairly fast.
|
|
||||||
"""
|
|
||||||
|
|
||||||
"""
|
|
||||||
Gets a spreadsheet object from a sheet id
|
|
||||||
"""
|
|
||||||
def getSpreadsheet(sheet_service, sheet_id):
|
|
||||||
#Get the spreadsheet
|
|
||||||
spreadsheet = sheet_service.spreadsheets().get(spreadsheetId=sheet_id).execute()
|
|
||||||
|
|
||||||
#And let him have it!
|
|
||||||
return spreadsheet
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
Gets the names of every page in a spreadsheet.
|
|
||||||
"""
|
|
||||||
def getPageNames(spreadsheet):
|
|
||||||
pageNames = [sheet["properties"]["title"] for sheet in spreadsheet["sheets"]]
|
|
||||||
return pageNames
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
Gets the contents of a page in its entirety, as a 2d array.
|
|
||||||
TODO: Make this take a spreadsheet object.
|
|
||||||
Unfortunately, the spreadsheet object doc is literally 20k lines long of
|
|
||||||
poorly formatted text(seriously, what the fuck)
|
|
||||||
"""
|
|
||||||
def getPageContents(sheet_service, sheet_id, pageName, range="$A1$1:$YY"):
|
|
||||||
sheet_range = pageName + "!" + range
|
|
||||||
values = sheet_service.spreadsheets().values()
|
|
||||||
result = values.get(spreadsheetId=sheet_id, range=sheet_range).execute()
|
|
||||||
|
|
||||||
return result.get('values', [])
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
Gets all pages as 2d arrays. from spreadsheet.
|
|
||||||
Pages are appended, in order.
|
|
||||||
So basically, you get an array of 2d arrays representing spreadsheet pages
|
|
||||||
"""
|
|
||||||
def getAllPageValues(sheet_service, sheet_id):
|
|
||||||
#Get all page names
|
|
||||||
pageNames = getPageNames(getSpreadsheet(sheet_service, sheet_id))
|
|
||||||
|
|
||||||
#Get values for each page
|
|
||||||
pageContents = [getPageContents(sheet_service, sheet_id, name, range="A2:D") for name in pageNames]
|
|
||||||
|
|
||||||
return pageContents
|
|
||||||
|
|
||||||
66
SlackUtil.py
66
SlackUtil.py
|
|
@ -1,66 +0,0 @@
|
||||||
from time import sleep
|
|
||||||
import re #Regular expressions
|
|
||||||
|
|
||||||
"""
|
|
||||||
Slack helpers. Separated for compartmentalization
|
|
||||||
"""
|
|
||||||
|
|
||||||
DEFAULT_USERNAME = "cylon" #NICE MEME
|
|
||||||
|
|
||||||
"""
|
|
||||||
Sends message with "text" as its content to the channel that message came from
|
|
||||||
"""
|
|
||||||
def reply(slack, msg, text, username=DEFAULT_USERNAME):
|
|
||||||
channel = msg['channel']
|
|
||||||
slack.api_call("chat.postMessage", channel=channel, text=text, username=username)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
Returns whether or not msg came from bot
|
|
||||||
"""
|
|
||||||
def isBotMessage(msg):
|
|
||||||
return ("bot_id" in msg or "user" not in msg)
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
Generator that yields messages from slack.
|
|
||||||
Messages are in standard api format, look it up.
|
|
||||||
Checks on 2 second intervals (may be changed)
|
|
||||||
"""
|
|
||||||
def messageFeed(slack):
|
|
||||||
if slack.rtm_connect():
|
|
||||||
print("Waiting for messages")
|
|
||||||
while True:
|
|
||||||
sleep(2)
|
|
||||||
update = slack.rtm_read()
|
|
||||||
for item in update:
|
|
||||||
if item['type'] == 'message':
|
|
||||||
yield item
|
|
||||||
|
|
||||||
print("Critical slack connection failure")
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
Returns whether or not user has configured profile
|
|
||||||
"""
|
|
||||||
def isValidProfile(user):
|
|
||||||
return ('profile' in user['user'] and
|
|
||||||
'first_name' in user['user']['profile'] and
|
|
||||||
'last_name' in user['user']['profile'])
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
Gets the user info for whoever is first mentioned in the message,
|
|
||||||
or None if no mention is made
|
|
||||||
"""
|
|
||||||
def getForUser(slack, msg):
|
|
||||||
m_re = re.compile(".*?<@([A-Z0-9]*?)>")
|
|
||||||
mention_match = m_re.match(msg['text'])
|
|
||||||
if mention_match is not None:
|
|
||||||
mention_id = mention_match.group(1)
|
|
||||||
return slack.api_call("users.info", user=mention_id)
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
@ -1,93 +0,0 @@
|
||||||
"""
|
|
||||||
This file handles conversion of users scrolls into numbers
|
|
||||||
"""
|
|
||||||
|
|
||||||
import re
|
|
||||||
from SlackUtil import reply
|
|
||||||
|
|
||||||
p = re.compile("truescroll\\s*(\d*)$")
|
|
||||||
|
|
||||||
def handleTrueScrollMsg(slack, msg):
|
|
||||||
text = msg['text']
|
|
||||||
response = None
|
|
||||||
|
|
||||||
m = p.match(text)
|
|
||||||
if not m:
|
|
||||||
response = "Could not parse your query. Please invoke as \"truescroll <scroll#>\""
|
|
||||||
else:
|
|
||||||
num = m.group(1)
|
|
||||||
|
|
||||||
|
|
||||||
brotherScroll = getBrotherScroll(num)
|
|
||||||
trueScroll = getTrueScroll(num)
|
|
||||||
|
|
||||||
#Correct for 666 (thanks TREVOR)
|
|
||||||
#Offset broscroll by one if >= 666
|
|
||||||
if int(brotherScroll) >= 666:
|
|
||||||
brotherScroll = getBrotherScroll(str(int(num) + 1))
|
|
||||||
|
|
||||||
#Offset truescroll in opposite direction
|
|
||||||
if int(num) > 666:
|
|
||||||
trueScroll = str(int(trueScroll) - 1)
|
|
||||||
|
|
||||||
|
|
||||||
#Memes
|
|
||||||
if int(num) == 666:
|
|
||||||
trueScroll = "spookiest"
|
|
||||||
elif "3" in num:
|
|
||||||
trueScroll = "worst"
|
|
||||||
|
|
||||||
response = "The brother with scroll {0} is in fact the {1} brother to sign\n"
|
|
||||||
response += "The {2} brother to sign will have scroll {3}\n"
|
|
||||||
|
|
||||||
response = response.format(num, trueScroll, num, brotherScroll)
|
|
||||||
|
|
||||||
reply(slack, msg, response, username = "scrollbot")
|
|
||||||
|
|
||||||
|
|
||||||
brotherNums = [str(x) for x in range(10) if (not x == 3)]
|
|
||||||
trueNums = [str(x) for x in range(10)]
|
|
||||||
|
|
||||||
#Returns string
|
|
||||||
def getTrueScroll(brotherScroll):
|
|
||||||
return convertBase(brotherScroll, brotherNums, trueNums)
|
|
||||||
|
|
||||||
|
|
||||||
#Returns string
|
|
||||||
def getBrotherScroll(trueScroll):
|
|
||||||
return convertBase(trueScroll, trueNums, brotherNums)
|
|
||||||
|
|
||||||
|
|
||||||
#Returns string
|
|
||||||
def convertBase(numStr, srcBaseNums, targBaseNums):
|
|
||||||
#Returns int value
|
|
||||||
def numberFromBase(ns, numerals):
|
|
||||||
base = len(numerals)
|
|
||||||
basePower = 1
|
|
||||||
total = 0
|
|
||||||
|
|
||||||
#Go by character.
|
|
||||||
for c in ns[::-1]:
|
|
||||||
try:
|
|
||||||
digitVal = numerals.index(c)
|
|
||||||
total += digitVal * basePower
|
|
||||||
except ValueError:
|
|
||||||
total += 0
|
|
||||||
|
|
||||||
basePower = basePower * base
|
|
||||||
|
|
||||||
return total
|
|
||||||
|
|
||||||
#Returns string, each elt is the corresponding numeral
|
|
||||||
def numberToBase(n, numerals):
|
|
||||||
if n==0:
|
|
||||||
return [0]
|
|
||||||
digits = []
|
|
||||||
while n:
|
|
||||||
digVal = int(n % len(numerals))
|
|
||||||
digits.append(numerals[digVal])
|
|
||||||
n /= len(numerals)
|
|
||||||
n = int(n)
|
|
||||||
return "".join(digits[::-1])
|
|
||||||
|
|
||||||
return numberToBase(numberFromBase(numStr, srcBaseNums), targBaseNums)
|
|
||||||
112
WaitonBot.py
112
WaitonBot.py
|
|
@ -1,112 +0,0 @@
|
||||||
import GoogleApi as google#For read drive
|
|
||||||
from slackclient import SlackClient#Obvious
|
|
||||||
from SlackUtil import *
|
|
||||||
|
|
||||||
from WaitonUtil import handleWaitonMsg #For waitons
|
|
||||||
from ScrollUtil import handleScrollMsg #For scrolls
|
|
||||||
from TrueScrollUtil import handleTrueScrollMsg #For true scrolls
|
|
||||||
from kong import handleKongMsg
|
|
||||||
import re
|
|
||||||
|
|
||||||
#Read api token from file
|
|
||||||
apifile = open("apitoken.txt", 'r')
|
|
||||||
SLACK_API = next(apifile).strip()
|
|
||||||
apifile.close();
|
|
||||||
|
|
||||||
#Read killswitch from file
|
|
||||||
killswitchfile = open("killswitch.txt", 'r')
|
|
||||||
killswitch = next(killswitchfile).strip()
|
|
||||||
killswitchfile.close()
|
|
||||||
|
|
||||||
#Set default username.
|
|
||||||
|
|
||||||
#Authenticate, get sheets service. Done globally so we dont have to do this
|
|
||||||
#every fucking time, which is probably a bad idea
|
|
||||||
sheet_credentials = google.get_sheets_credentials()
|
|
||||||
sheet_service = google.get_sheets_service(sheet_credentials)
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
Insults the scrub who tried to use the bot without setting up slack.
|
|
||||||
If for_user is supplied, assumes user was fine and insults for_user
|
|
||||||
|
|
||||||
Also provides helpful info for how to avoid future disgrace, but thats like, tertiary
|
|
||||||
"""
|
|
||||||
def handleProfilelessScum(slack, msg, user, for_user=None):
|
|
||||||
f_response = None
|
|
||||||
if for_user:
|
|
||||||
user_name = user['user']['name']
|
|
||||||
for_name = for_user['user']['name']
|
|
||||||
f_response = "Hey {0}, tell {1} to set up his fucking profile. Like, first and last name, and stuff".format(user_name, for_name)
|
|
||||||
else:
|
|
||||||
f_response = "Set up your profile before talking to me, scum.\n\nThat is to say, fill out your first and last name in your slack user profile! Please use what would be on the waiton list (IE your proper name, not a nickname)."
|
|
||||||
reply(slack, msg, f_response)
|
|
||||||
|
|
||||||
|
|
||||||
waiton_pattern = re.compile("^waiton")
|
|
||||||
scroll_pattern = re.compile("^scroll")
|
|
||||||
true_scroll_pattern = re.compile("^truescroll")
|
|
||||||
housejob_pattern = re.compile("^(house)?job")
|
|
||||||
kong_pattern = re.compile("^kong")
|
|
||||||
|
|
||||||
def main():
|
|
||||||
#Init slack
|
|
||||||
slack = SlackClient(SLACK_API)
|
|
||||||
print(slack)
|
|
||||||
|
|
||||||
slack.api_call("chat.postMessage", channel="@jacobhenry", text="I'm back baby!")
|
|
||||||
|
|
||||||
feed = messageFeed(slack)
|
|
||||||
for msg in feed:
|
|
||||||
#Check not bot
|
|
||||||
if isBotMessage(msg):
|
|
||||||
print("Message skipped. Reason: bot")
|
|
||||||
continue
|
|
||||||
|
|
||||||
#get user info
|
|
||||||
userid = msg['user']
|
|
||||||
user = slack.api_call("users.info", user=userid)
|
|
||||||
|
|
||||||
#If a mention is found, assign for_user
|
|
||||||
for_user = getForUser(slack, msg)
|
|
||||||
|
|
||||||
#Handle Message
|
|
||||||
text = msg['text'].lower()
|
|
||||||
msg['text'] = text
|
|
||||||
if not isValidProfile(user):#invalid profile
|
|
||||||
print("Received profileless")
|
|
||||||
handleProfilelessScum(slack, msg, user)
|
|
||||||
|
|
||||||
elif for_user and not isValidProfile(for_user):#invalid for_user profile
|
|
||||||
print("Received for profileless")
|
|
||||||
handleProfilelessScum(slack, msg, user, for_user)
|
|
||||||
|
|
||||||
elif waiton_pattern.match(text):
|
|
||||||
print("Received waiton from " + user['user']['name'])
|
|
||||||
handleWaitonMsg(slack, sheet_service, msg, user, for_user)
|
|
||||||
|
|
||||||
elif scroll_pattern.match(text):
|
|
||||||
print("Received scroll from " + user['user']['name'])
|
|
||||||
handleScrollMsg(slack, msg)
|
|
||||||
|
|
||||||
elif true_scroll_pattern.match(text):
|
|
||||||
print("Received true scroll from " + user['user']['name'])
|
|
||||||
handleTrueScrollMsg(slack, msg)
|
|
||||||
|
|
||||||
elif housejob_pattern.match(text):
|
|
||||||
print("Received housejob from " + user['user']['name'])
|
|
||||||
reply(slack, msg, "I cannot do that (yet)", username="sadbot")
|
|
||||||
|
|
||||||
elif kong_pattern.match(text):
|
|
||||||
print("Received kong from " + user['user']['name'])
|
|
||||||
handleKongMsg(slack, msg)
|
|
||||||
|
|
||||||
elif killswitch == msg['text'].lower():
|
|
||||||
reply(slack, msg, "as you wish...", username="rip bot")
|
|
||||||
break
|
|
||||||
|
|
||||||
else:
|
|
||||||
print("Message skipped. Reason: no command found")
|
|
||||||
|
|
||||||
#run main
|
|
||||||
main()
|
|
||||||
138
WaitonUtil.py
138
WaitonUtil.py
|
|
@ -1,138 +0,0 @@
|
||||||
import SheetUtil #For read from waiton sheet
|
|
||||||
from tabulate import tabulate
|
|
||||||
from SlackUtil import reply
|
|
||||||
"""
|
|
||||||
This file contains util for waiton polling.
|
|
||||||
Only really kept separate for neatness sake.
|
|
||||||
"""
|
|
||||||
|
|
||||||
#ID of waiton sheet on drive
|
|
||||||
#WAITON_SHEET_ID = "1J3WDe-OI7YjtDv6mMlM1PN3UlfZo8_y9GBVNBEPwOhE" #A term 2016
|
|
||||||
#WAITON_SHEET_ID = "1I4keh9cIt0x-WwZAdnBsZefSZV-tMIAy37r2NLUOLh4" #First week b term
|
|
||||||
WAITON_SHEET_ID = "1jnLXo_QhZWId84gAVC3rzzo26HmTo_cs2FqviyU6_mw" #All of b term
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
Pulls waiton data from the spreadsheet.
|
|
||||||
Returns waitons as list of objects, each of form
|
|
||||||
{
|
|
||||||
name: <brothername>,
|
|
||||||
date: <date as string>,
|
|
||||||
meal: <meal as string>
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
def getWaitons(sheet_service):
|
|
||||||
#Propogate dates to each row
|
|
||||||
def fixDates(values):
|
|
||||||
curr_date = None
|
|
||||||
curr_dow = None
|
|
||||||
last_row = None
|
|
||||||
for row in values:
|
|
||||||
date_col = row[0]
|
|
||||||
|
|
||||||
#Update date if it is more or less a date
|
|
||||||
if "/" in date_col:
|
|
||||||
curr_date = date_col
|
|
||||||
|
|
||||||
#Update previous row
|
|
||||||
if curr_date is not None:
|
|
||||||
last_row[0] = curr_dow + " - " + curr_date
|
|
||||||
|
|
||||||
#Update DOW now that previous row will not be affected "" != date_col:
|
|
||||||
if "/" not in date_col and "" != date_col:
|
|
||||||
curr_dow = date_col
|
|
||||||
|
|
||||||
#Cycle last_row
|
|
||||||
last_row = row
|
|
||||||
|
|
||||||
#Fix the last row
|
|
||||||
if last_row is not None:
|
|
||||||
last_row[0] = curr_dow + " - " + curr_date
|
|
||||||
|
|
||||||
#Propogate meal data to each row
|
|
||||||
def fixMeals(values):
|
|
||||||
curr_meal = None
|
|
||||||
for row in values:
|
|
||||||
#Update curr meal:
|
|
||||||
if row[1] != "" and row[1] not in ["starts at 4:30p", "Midnight"]:
|
|
||||||
curr_meal = row[1]
|
|
||||||
|
|
||||||
if curr_meal is not None:
|
|
||||||
row[1] = curr_meal
|
|
||||||
|
|
||||||
#Helper to remove steward rows
|
|
||||||
def filterStewards(values):
|
|
||||||
return [row for row in values if len(row) > 2]
|
|
||||||
|
|
||||||
#Helper to remove empty rows (IE no assignees)
|
|
||||||
def filterUnset(values):
|
|
||||||
return [row for row in values if "---" not in row[2]]
|
|
||||||
|
|
||||||
pageContents = SheetUtil.getAllPageValues(sheet_service, WAITON_SHEET_ID)
|
|
||||||
|
|
||||||
#Filter junk rows
|
|
||||||
pageContents = [filterStewards(x) for x in pageContents]
|
|
||||||
pageContents = [filterUnset(x) for x in pageContents]
|
|
||||||
|
|
||||||
#Fix stuff (make each have full info)
|
|
||||||
for pc in pageContents:
|
|
||||||
fixDates(pc)
|
|
||||||
fixMeals(pc)
|
|
||||||
|
|
||||||
#Merge, using property of python list concatenation via add (+)
|
|
||||||
allWaitons = sum(pageContents, [])
|
|
||||||
|
|
||||||
#Parse to objects
|
|
||||||
waitonObjects = [{
|
|
||||||
"name": row[2],
|
|
||||||
"date": row[0],
|
|
||||||
"meal": row[1]
|
|
||||||
} for row in allWaitons]
|
|
||||||
|
|
||||||
return waitonObjects
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
Takes a slack context, a message to reply to, and a user to look up waitons for.
|
|
||||||
Additionally, takes a for_user flag for message formatting
|
|
||||||
IE "user" asked for "for_user" waitons, so blah blah blah
|
|
||||||
"""
|
|
||||||
def handleWaitonMsg(slack, sheet_service, msg, user, for_user=None):
|
|
||||||
"""
|
|
||||||
Filters to waitons with the given name
|
|
||||||
"""
|
|
||||||
def filterWaitons(waitons, first, last):
|
|
||||||
def valid(waiton):
|
|
||||||
return first in waiton["name"] or last in waiton["name"]
|
|
||||||
|
|
||||||
return [w for w in waitons if valid(w)]
|
|
||||||
|
|
||||||
"""
|
|
||||||
Formats waitons for output
|
|
||||||
"""
|
|
||||||
def formatWaitons(waitons):
|
|
||||||
waitonRows = [(w["name"], w["date"], w["meal"]) for w in waitons]
|
|
||||||
return tabulate(waitonRows)
|
|
||||||
|
|
||||||
#Create format string
|
|
||||||
response = ( "{0} asked for waiton information{1}, so here are his waitons:\n"
|
|
||||||
"{2}")
|
|
||||||
|
|
||||||
#Get names of caller
|
|
||||||
requestor_first = user['user']['profile']['first_name']
|
|
||||||
requestor_last = user['user']['profile']['last_name']
|
|
||||||
|
|
||||||
#Get names of target user
|
|
||||||
for_first = (for_user or user)['user']['profile']['first_name']
|
|
||||||
for_last = (for_user or user)['user']['profile']['last_name']
|
|
||||||
|
|
||||||
#Get waitons for target user
|
|
||||||
waitons = getWaitons(sheet_service)
|
|
||||||
waiton_string = formatWaitons( filterWaitons(waitons, for_first, for_last))
|
|
||||||
|
|
||||||
f_response = response.format(requestor_first, " for {0}".format(for_first) if for_user else "", waiton_string)
|
|
||||||
|
|
||||||
reply(slack, msg, f_response, username="mealbot")
|
|
||||||
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
slackclient
|
slackclient
|
||||||
python-Levenshtein
|
python-Levenshtein
|
||||||
fuzzywuzzy
|
fuzzywuzzy
|
||||||
|
httplib2
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
"""
|
||||||
|
Examples provided by google for using their api.
|
||||||
|
Very slightly modified by me to easily just get credentials
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
|
||||||
|
from googleapiclient.discovery import build
|
||||||
|
from httplib2 import Http
|
||||||
|
from oauth2client import file, client, tools
|
||||||
|
|
||||||
|
# If modifying these scopes, delete your previously saved credentials
|
||||||
|
# at ~/.credentials/sheets.googleapis.com-python-quickstart.json
|
||||||
|
SCOPES = 'https://www.googleapis.com/auth/spreadsheets.readonly'
|
||||||
|
APPLICATION_NAME = 'My Project'
|
||||||
|
|
||||||
|
|
||||||
|
def _init_sheets_service():
|
||||||
|
store = file.Storage('token.json')
|
||||||
|
creds = store.get()
|
||||||
|
if not creds or creds.invalid:
|
||||||
|
flow = client.flow_from_clientsecrets('sheets_credentials.json', SCOPES)
|
||||||
|
creds = tools.run_flow(flow, store)
|
||||||
|
service = build('sheets', 'v4', http=creds.authorize(Http()))
|
||||||
|
return service
|
||||||
|
|
||||||
|
|
||||||
|
_global_sheet_service = _init_sheets_service()
|
||||||
|
|
||||||
|
|
||||||
|
# range should be of format 'SHEET NAME!A1:Z9'
|
||||||
|
def get_sheet_range(spreadsheet_id, range):
|
||||||
|
"""
|
||||||
|
Gets an array of the desired table
|
||||||
|
"""
|
||||||
|
result = _global_sheet_service.spreadsheets().values().get(spreadsheetId=spreadsheet_id,
|
||||||
|
range=range).execute()
|
||||||
|
values = result.get('values', [])
|
||||||
|
if not values:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return values
|
||||||
|
|
||||||
|
|
||||||
|
def get_calendar_credentials():
|
||||||
|
"""Gets valid user credentials from storage.
|
||||||
|
|
||||||
|
If nothing has been stored, or if the stored credentials are invalid,
|
||||||
|
the OAuth2 flow is completed to obtain the new credentials.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Credentials, the obtained credential.
|
||||||
|
"""
|
||||||
|
home_dir = os.path.expanduser('~')
|
||||||
|
credential_dir = os.path.join(home_dir, '.credentials')
|
||||||
|
if not os.path.exists(credential_dir):
|
||||||
|
os.makedirs(credential_dir)
|
||||||
|
credential_path = os.path.join(credential_dir, 'calendar-python-quickstart.json')
|
||||||
|
|
||||||
|
store = oauth2client.file.Storage(credential_path)
|
||||||
|
credentials = store.get()
|
||||||
|
if not credentials or credentials.invalid:
|
||||||
|
flow = client.flow_from_clientsecrets(CLIENT_SECRET_FILE, SCOPES)
|
||||||
|
flow.user_agent = APPLICATION_NAME
|
||||||
|
if flags:
|
||||||
|
credentials = tools.run_flow(flow, store, flags)
|
||||||
|
else: # Needed only for compatibility with Python 2.6
|
||||||
|
credentials = tools.run(flow, store)
|
||||||
|
print('Storing credentials to ' + credential_path)
|
||||||
|
return credentials
|
||||||
111
kong.py
111
kong.py
|
|
@ -1,111 +0,0 @@
|
||||||
import threading
|
|
||||||
import re
|
|
||||||
from SlackUtil import reply
|
|
||||||
from time import sleep
|
|
||||||
|
|
||||||
DEFAULT_SONG = "dk_rap_classic"
|
|
||||||
DEFAULT_DELAY = 2000
|
|
||||||
|
|
||||||
#Patterns
|
|
||||||
patterns = {}
|
|
||||||
patterns['start'] = re.compile(r"#STARTSONG\s+(.*?)$")
|
|
||||||
patterns['time'] = re.compile(r"#TIMEPERLINE\s+(\d*)$")
|
|
||||||
patterns['rest'] = re.compile(r"#REST")
|
|
||||||
patterns['end'] = re.compile(r"#ENDSONG")
|
|
||||||
|
|
||||||
#SongContents
|
|
||||||
lyric_lines = []
|
|
||||||
f = open("lyrics.txt")
|
|
||||||
for line in f:
|
|
||||||
lyric_lines += [line]
|
|
||||||
f.close()
|
|
||||||
|
|
||||||
|
|
||||||
class SongThread(threading.Thread):
|
|
||||||
def __init__(self, callback, song_name):
|
|
||||||
super().__init__()
|
|
||||||
self.song_name = song_name
|
|
||||||
self.callback = callback
|
|
||||||
self.delay = DEFAULT_DELAY
|
|
||||||
self.daemon = True
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
playing = False
|
|
||||||
|
|
||||||
for line in lyric_lines:
|
|
||||||
line = line.strip()
|
|
||||||
if line == "":
|
|
||||||
continue
|
|
||||||
|
|
||||||
#Navigate to song start
|
|
||||||
if not playing:
|
|
||||||
m = patterns['start'].match(line)
|
|
||||||
print(line)
|
|
||||||
if m:
|
|
||||||
if m.group(1) == self.song_name:
|
|
||||||
playing = True
|
|
||||||
continue
|
|
||||||
|
|
||||||
#Play loop
|
|
||||||
else:
|
|
||||||
#Config
|
|
||||||
m = patterns['time'].match(line)
|
|
||||||
if m:
|
|
||||||
self.delay = int(m.group(1))
|
|
||||||
continue
|
|
||||||
|
|
||||||
#Rest line
|
|
||||||
m = patterns['rest'].match(line)
|
|
||||||
if m:
|
|
||||||
sleep(self.delay / 1000)
|
|
||||||
continue
|
|
||||||
|
|
||||||
#End song
|
|
||||||
m = patterns['end'].match(line)
|
|
||||||
if m:
|
|
||||||
return
|
|
||||||
|
|
||||||
#"sing" line
|
|
||||||
self.callback(line)
|
|
||||||
sleep(self.delay / 1000)
|
|
||||||
|
|
||||||
if not playing:
|
|
||||||
self.callback("Could not find song")
|
|
||||||
|
|
||||||
def getAllTitles():
|
|
||||||
titles = []
|
|
||||||
for line in lyric_lines:
|
|
||||||
m = patterns['start'].match(line)
|
|
||||||
if m:
|
|
||||||
titles += [m.group(1)]
|
|
||||||
|
|
||||||
return titles
|
|
||||||
|
|
||||||
request_pattern = re.compile(r"kong\s*(.*?)$")
|
|
||||||
|
|
||||||
def handleKongMsg(slack, msg):
|
|
||||||
#Get text
|
|
||||||
text = msg['text']
|
|
||||||
match = request_pattern.match(text)
|
|
||||||
|
|
||||||
#Make callback function
|
|
||||||
reply_callback = lambda response: reply(slack, msg, response, username="jukebot")
|
|
||||||
if match:
|
|
||||||
if match.group(1) == "list":
|
|
||||||
response = ""
|
|
||||||
for title in getAllTitles():
|
|
||||||
response = response + title
|
|
||||||
reply_callback(response)
|
|
||||||
|
|
||||||
elif match.group(1) != "":
|
|
||||||
st = SongThread(reply_callback, match.group(1))
|
|
||||||
st.start()
|
|
||||||
else:
|
|
||||||
st = SongThread(reply_callback, DEFAULT_SONG)
|
|
||||||
st.start()
|
|
||||||
|
|
||||||
else:
|
|
||||||
response = "Invoke as kong <songtitle:list>"
|
|
||||||
reply_callback(response)
|
|
||||||
|
|
||||||
|
|
||||||
69
lyrics.txt
69
lyrics.txt
|
|
@ -1,69 +0,0 @@
|
||||||
|
|
||||||
#STARTSONG dk_rap_classic
|
|
||||||
#TIMEPERLINE 750
|
|
||||||
So they're finally here, performing for you
|
|
||||||
If you know the words, you can join in too
|
|
||||||
Put your hands together, if you want to clap
|
|
||||||
As we take you through this monkey rap
|
|
||||||
|
|
||||||
DK
|
|
||||||
Donkey Kong
|
|
||||||
|
|
||||||
He's the leader of the bunch, you know him well
|
|
||||||
He's finally back to kick some tail
|
|
||||||
His coconut gun can fire in spurts
|
|
||||||
If he shoots ya, it's gonna hurt
|
|
||||||
He's bigger, faster, and stronger too
|
|
||||||
He's the first member of the DK crew
|
|
||||||
|
|
||||||
DK
|
|
||||||
Donkey Kong
|
|
||||||
DK
|
|
||||||
Donkey Kong is here
|
|
||||||
|
|
||||||
This Kong's got style, so listen up dudes
|
|
||||||
She can shrink in size, to suit her mood
|
|
||||||
She's quick and nimble when she needs to be
|
|
||||||
She can float through the air and climb up trees
|
|
||||||
If you choose her, you'll not choose wrong
|
|
||||||
With a skip and a hop, she's one cool Kong
|
|
||||||
|
|
||||||
DK
|
|
||||||
Donkey Kong
|
|
||||||
|
|
||||||
He has no style, he has no grace
|
|
||||||
This Kong has a funny face
|
|
||||||
He can handstand when he needs to
|
|
||||||
And stretch his arms out, just for you
|
|
||||||
Inflate himself just like a balloon
|
|
||||||
This crazy Kong just digs this tune
|
|
||||||
|
|
||||||
DK
|
|
||||||
Donkey Kong
|
|
||||||
DK
|
|
||||||
Donkey Kong is here
|
|
||||||
|
|
||||||
He's back again and about time too
|
|
||||||
And this time he's in the mood
|
|
||||||
He can fly real high with his jetpack on
|
|
||||||
With his pistols out, he's one tough Kong
|
|
||||||
He'll make you smile when he plays his tune
|
|
||||||
But Kremlings beware cause he's after you
|
|
||||||
|
|
||||||
DK
|
|
||||||
Donkey Kong
|
|
||||||
|
|
||||||
Finally, he's here for you
|
|
||||||
It's the last member of the DK crew
|
|
||||||
This Kong's so strong, it isn't funny
|
|
||||||
Can make a Kremling cry out for mummy
|
|
||||||
Can pick up a boulder with relative ease
|
|
||||||
Makes crushing rocks seem such a breeze
|
|
||||||
He may move slow, he can't jump high
|
|
||||||
But this Kong's one hell of a guy
|
|
||||||
|
|
||||||
C'mon Cranky, take it to the fridge!
|
|
||||||
|
|
||||||
Walnuts, peanuts, pineapple smells
|
|
||||||
Grapes, melons, oranges and coconut shells
|
|
||||||
#ENDSONG
|
|
||||||
|
|
@ -0,0 +1,82 @@
|
||||||
|
from collections import OrderedDict
|
||||||
|
|
||||||
|
import google_api as google # For read drive
|
||||||
|
from slackclient import SlackClient # Obvious
|
||||||
|
from slack_util import *
|
||||||
|
|
||||||
|
import scroll_util
|
||||||
|
import re
|
||||||
|
|
||||||
|
# Read api token from file
|
||||||
|
api_file = open("apitoken.txt", 'r')
|
||||||
|
SLACK_API = next(api_file).strip()
|
||||||
|
api_file.close()
|
||||||
|
|
||||||
|
# Read kill switch from file
|
||||||
|
kill_switch_file = open("killswitch.txt", 'r')
|
||||||
|
kill_switch = next(kill_switch_file).strip()
|
||||||
|
kill_switch_file.close()
|
||||||
|
|
||||||
|
# Authenticate, get sheets service. Done globally so we dont have to do this
|
||||||
|
# every fucking time, which is probably a bad idea
|
||||||
|
sheet_credentials = google.get_sheets_credentials()
|
||||||
|
sheet_service = google._init_sheets_service(sheet_credentials)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
wrapper = ClientWrapper()
|
||||||
|
|
||||||
|
# DEBUG: Add blanked handling
|
||||||
|
# wrapper.add_hook(".*", print)
|
||||||
|
|
||||||
|
# Add scroll handling
|
||||||
|
wrapper.add_hook(scroll_util.command_pattern, scroll_util.callback)
|
||||||
|
|
||||||
|
# Add kill switch
|
||||||
|
wrapper.add_hook(kill_switch, die)
|
||||||
|
|
||||||
|
wrapper.listen()
|
||||||
|
|
||||||
|
|
||||||
|
def die(*args):
|
||||||
|
print("Got kill switch")
|
||||||
|
exit()
|
||||||
|
|
||||||
|
|
||||||
|
class ClientWrapper(object):
|
||||||
|
def __init__(self):
|
||||||
|
# Init slack
|
||||||
|
self._slack = SlackClient(SLACK_API)
|
||||||
|
self._slack.rtm_send_message(channel="@jacob henry", message="I'm back baby!")
|
||||||
|
|
||||||
|
# Hooks go regex -> callback on (slack, msg, match)
|
||||||
|
self._hooks = OrderedDict()
|
||||||
|
|
||||||
|
def add_hook(self, pattern, callback):
|
||||||
|
self._hooks[re.compile(pattern)] = callback
|
||||||
|
|
||||||
|
def listen(self):
|
||||||
|
feed = message_stream(self._slack)
|
||||||
|
for msg in feed:
|
||||||
|
# We only care about standard messages, not subtypes, as those usually just channel activity
|
||||||
|
if msg.get("subtype"):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Handle Message
|
||||||
|
text = msg['text'].strip()
|
||||||
|
success = False
|
||||||
|
for regex, callback in self._hooks.items():
|
||||||
|
match = regex.match(text)
|
||||||
|
if match:
|
||||||
|
success = True
|
||||||
|
print("Matched on callback {}".format(callback))
|
||||||
|
callback(self._slack, msg, match)
|
||||||
|
break
|
||||||
|
|
||||||
|
if not success:
|
||||||
|
print("No hit on {}".format(text))
|
||||||
|
|
||||||
|
|
||||||
|
# run main
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
|
@ -9,7 +9,6 @@ do
|
||||||
echo "Press [CTRL+C] to stop..."
|
echo "Press [CTRL+C] to stop..."
|
||||||
sleep 1
|
sleep 1
|
||||||
python3 WaitonBot.py
|
python3 WaitonBot.py
|
||||||
sleep 3
|
sleep 1
|
||||||
python3 CallOfTheVoid.py
|
|
||||||
echo "Died. Updating and restarting..."
|
echo "Died. Updating and restarting..."
|
||||||
done
|
done
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
"""
|
||||||
|
This file contains util for scroll polling
|
||||||
|
Only really kept separate for neatness sake.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import re
|
||||||
|
from slack_util import reply
|
||||||
|
from fuzzywuzzy import fuzz
|
||||||
|
from fuzzywuzzy import process
|
||||||
|
|
||||||
|
# load the family tree
|
||||||
|
familyfile = open("sortedfamilytree.txt", 'r')
|
||||||
|
|
||||||
|
|
||||||
|
command_pattern = r"scroll\s+(.*)"
|
||||||
|
|
||||||
|
# Parse out
|
||||||
|
brother_match = re.compile(r"([0-9]*)~(.*)")
|
||||||
|
brothers = [brother_match.match(line) for line in familyfile]
|
||||||
|
brothers = [m for m in brothers if m]
|
||||||
|
brothers = [{
|
||||||
|
"scroll": int(m.group(1)),
|
||||||
|
"name": m.group(2)
|
||||||
|
} for m in brothers]
|
||||||
|
|
||||||
|
"""
|
||||||
|
Attempts to look up a user by scroll
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def callback(slack, msg, match):
|
||||||
|
# Get the query
|
||||||
|
query = match.group(1).strip()
|
||||||
|
|
||||||
|
# Try to get as int or by name
|
||||||
|
try:
|
||||||
|
sn = int(query)
|
||||||
|
result = find_by_scroll(sn)
|
||||||
|
except ValueError:
|
||||||
|
result = find_by_name(query)
|
||||||
|
if result:
|
||||||
|
result = "Brother {} has scroll {}".format(result["name"], result["scroll"])
|
||||||
|
else:
|
||||||
|
result = "Couldn't find brother {}".format(query)
|
||||||
|
|
||||||
|
# Respond
|
||||||
|
reply(slack, msg, result)
|
||||||
|
|
||||||
|
|
||||||
|
def find_by_scroll(scroll):
|
||||||
|
for b in brothers:
|
||||||
|
if b["scroll"] == scroll:
|
||||||
|
return b
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def find_by_name(name):
|
||||||
|
# coerce name into dict form
|
||||||
|
name = {"name": name}
|
||||||
|
|
||||||
|
# Do fuzzy match
|
||||||
|
return process.extractOne(name, brothers, processor=lambda b: b["name"])[0]
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
"""
|
||||||
|
Slack helpers. Separated for compartmentalization
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
def reply(slack, msg, text):
|
||||||
|
"""
|
||||||
|
Sends message with "text" as its content to the channel that message came from
|
||||||
|
"""
|
||||||
|
channel = msg['channel']
|
||||||
|
thread_id = msg['ts']
|
||||||
|
slack.rtm_send_message(channel=channel, message=text, thread=thread_id)
|
||||||
|
|
||||||
|
|
||||||
|
def message_stream(slack):
|
||||||
|
"""
|
||||||
|
Generator that yields messages from slack.
|
||||||
|
Messages are in standard api format, look it up.
|
||||||
|
Checks on 2 second intervals (may be changed)
|
||||||
|
"""
|
||||||
|
# Do forever
|
||||||
|
while True:
|
||||||
|
if slack.rtm_connect(with_team_state=False, auto_reconnect=True):
|
||||||
|
print("Waiting for messages")
|
||||||
|
while True:
|
||||||
|
sleep(2)
|
||||||
|
update = slack.rtm_read()
|
||||||
|
for item in update:
|
||||||
|
if item.get('type') == 'message':
|
||||||
|
yield item
|
||||||
|
|
||||||
|
sleep(15)
|
||||||
|
print("Connection failed - retrying")
|
||||||
1539
sortedfamilytree.txt
1539
sortedfamilytree.txt
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue