Add own_pancakes

This commit is contained in:
Midgard 2020-12-09 14:25:41 +01:00
parent 2cf0f5a580
commit 228e44838a
Signed by: midgard
GPG key ID: 511C112F1331BBB4

View file

@ -7,7 +7,7 @@ import datetime
import threading import threading
from time import sleep from time import sleep
import json import json
from typing import Optional from typing import Optional, Mapping, Set
import mattermost import mattermost
import mattermost.ws import mattermost.ws
@ -15,6 +15,7 @@ SERVER = "mattermost.zeus.gent"
TEAM_NAME = "zeus" TEAM_NAME = "zeus"
CHAN_NAME = "pannenkoeken" CHAN_NAME = "pannenkoeken"
EMOJI_NAME = "pancakes" EMOJI_NAME = "pancakes"
DOUBLE_EMOJI_NAME = "own_pancakes"
TAGGERS = [ TAGGERS = [
# Board # Board
"flynn", "flynn",
@ -34,6 +35,7 @@ TOKEN = os.getenv("MM_ACCESS_TOKEN")
USER = os.getenv("MM_USERNAME") USER = os.getenv("MM_USERNAME")
PASSWORD = os.getenv("MM_PASSWORD") PASSWORD = os.getenv("MM_PASSWORD")
# Set to False to disable reacting with an emoji for the count
CONFIRMATION_EMOJI = True CONFIRMATION_EMOJI = True
@ -48,7 +50,7 @@ if since_arg_i:
clean = "--clean" in sys.argv[1:] clean = "--clean" in sys.argv[1:]
live = "--live" in sys.argv[1:] live = "--live" in sys.argv[1:]
if "--no-confirm" in sys.argv[1:]: if "--no-confirm" in sys.argv[1:]:
CONFIRMATION_EMOJI = None CONFIRMATION_EMOJI = False
@ -124,12 +126,46 @@ def to_mm_timestamp(dt):
def reaction_qualifies(reaction): def reaction_qualifies(reaction):
return reaction["emoji_name"] == EMOJI_NAME and reaction["user_id"] in tagger_ids if reaction["user_id"] not in tagger_ids:
return 0
if reaction["emoji_name"] == EMOJI_NAME:
return 1
if reaction["emoji_name"] == DOUBLE_EMOJI_NAME:
return 2
return 0
awarded = {} # awarded[awardee][post_id]: set of verifiers def post_score(awardee_id, post_id, awarder_id):
def award_if_appropriate(reaction): return max(awarded[awardee_id][post_id][awarder_id], default=0)
if not reaction_qualifies(reaction):
def emit_change_line(post, awardee_id, awarder_id, prev_score, score):
if score == prev_score:
return
awardee = get_username(awardee_id)
awarder = get_username(awarder_id)
post_time = parse_mm_timestamp(post["create_at"]).isoformat(timespec="microseconds")
if score == 0:
message = f"{awarder} retracted their verification"
elif prev_score == 0:
message = f"{awarder} verified with score {score}"
else:
message = f"{awarder} updated their verification's score from {prev_score} to {score}"
print(f"{awardee} {post['id']} at {post_time}: {message}", flush=True)
# awarded[awardee][post_id][verifier]: set of values
awarded: Mapping[str, Mapping[str, Mapping[str, Set[int]]]] = \
defaultdict(lambda: defaultdict(lambda: defaultdict(set)))
def process_change(reaction, action):
value = reaction_qualifies(reaction)
if value == 0:
return return
post = get_post(reaction["post_id"]) post = get_post(reaction["post_id"])
@ -139,48 +175,25 @@ def award_if_appropriate(reaction):
awardee_id = post["user_id"] awardee_id = post["user_id"]
awarder_id = reaction["user_id"] awarder_id = reaction["user_id"]
if awardee_id not in awarded: prev_score = post_score(awardee_id, post["id"], awarder_id)
awarded[awardee_id] = {} action(awarded[awardee_id][post["id"]][awarder_id], value)
if post["id"] not in awarded[awardee_id]: score = post_score(awardee_id, post["id"], awarder_id)
awarded[awardee_id][post["id"]] = set()
if awarder_id in awarded[awardee_id][post["id"]]:
# We already knew that this user verified this post
return
awarded[awardee_id][post["id"]].add(awarder_id)
reaction_time = parse_mm_timestamp(reaction["create_at"]).isoformat(timespec="microseconds")
post_time = parse_mm_timestamp(reaction["create_at"]).isoformat(timespec="microseconds")
awardee = get_username(awardee_id)
awarder = get_username(awarder_id)
print(f"{awardee} {post['id']} at {post_time} verified by {awarder} at {reaction_time}", flush=True)
emit_change_line(post, awardee_id, awarder_id, prev_score, score)
update_confirmation(post["id"]) update_confirmation(post["id"])
def award_if_appropriate(reaction):
process_change(
reaction,
lambda values_set, value: values_set.add(value)
)
def retract_if_appropriate(reaction): def retract_if_appropriate(reaction):
if not reaction_qualifies(reaction): process_change(
return reaction,
lambda values_set, value: values_set.discard(value)
post = get_post(reaction["post_id"]) )
if parse_mm_timestamp(post["create_at"]) < SINCE:
return
awardee_id = post["user_id"]
awarder_id = reaction["user_id"]
awarded[awardee_id][post["id"]].discard(awarder_id)
if not awarded[awardee_id][post["id"]]:
del awarded[awardee_id][post["id"]]
if not awarded[awardee_id]:
del awarded[awardee_id]
awardee = get_username(awardee_id)
awarder = get_username(awarder_id)
print(f"{awardee} {post['id']} verification removed by {awarder}", flush=True)
update_confirmation(post["id"])
def get_posts_for_channel(mmapi, channel_id, since, **kwargs): def get_posts_for_channel(mmapi, channel_id, since, **kwargs):
@ -222,6 +235,19 @@ def persevere(f, backoff=1):
sleep(backoff) sleep(backoff)
# awarded[awardee][post_id][verifier]: set of values
def count_verifications(user_id):
return sum(
max(
(
max(values, default=0) for values in post_verifications.values()
),
default=0
)
for post_verifications in awarded[user_id].values()
)
def update_confirmation(post_id): def update_confirmation(post_id):
if not CONFIRMATION_EMOJI: if not CONFIRMATION_EMOJI:
return return
@ -229,7 +255,7 @@ def update_confirmation(post_id):
post = get_post(post_id, force_fetch=True) post = get_post(post_id, force_fetch=True)
remove_reactions_from_post(post) remove_reactions_from_post(post)
new_count = len(awarded.get(post["user_id"], [])) new_count = count_verifications(post["user_id"])
if new_count > 0: if new_count > 0:
persevere(lambda: mm.create_reaction(our_user_id, post_id, confirmation_emoji_name(new_count))) persevere(lambda: mm.create_reaction(our_user_id, post_id, confirmation_emoji_name(new_count)))
@ -268,8 +294,8 @@ def handle_live():
if clean: if clean:
for post in get_posts_for_channel(mm, channel, SINCE): for _post in get_posts_for_channel(mm, channel, SINCE):
remove_reactions_from_post(post) remove_reactions_from_post(_post)
else: else:
# Note: skipping this step and updating an existing file would be dangerous: you would miss revocations that happened while not listening. # Note: skipping this step and updating an existing file would be dangerous: you would miss revocations that happened while not listening.