Add own_pancakes
This commit is contained in:
parent
2cf0f5a580
commit
228e44838a
1 changed files with 72 additions and 46 deletions
|
@ -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.
|
||||||
|
|
Loading…
Reference in a new issue