From 76f65eb9d3bf043126cc91904ca4df74ec24abbe Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 17 Feb 2019 15:57:25 +0200 Subject: [PATCH] Add support for anonymizing select users and add content storage options --- base-config.yaml | 14 ++++++++++++- karma/bot.py | 52 ++++++++++++++++++++++++++++++++++-------------- 2 files changed, 50 insertions(+), 16 deletions(-) diff --git a/base-config.yaml b/base-config.yaml index 15cc35c..ddc4aee 100644 --- a/base-config.yaml +++ b/base-config.yaml @@ -1,4 +1,16 @@ -# Whether or not everyone can vote +# Whether or not to show content of messages in !karma best. +show_content: true +# Whether or not to store content of messages. +# false - no storage +# partial - store one line +# full - store whole content +store_content: partial + +# SHA1 hashes of users who have opted out. +opt_out: +- 783660685956dadb4fa2f1aeac54c8525e8b75b7 + +# Whether or not everyone can vote. democracy: true # The list of people to filter. # If democracy is enabled, this is a blacklist. Otherwise this is a whitelist. diff --git a/karma/bot.py b/karma/bot.py index 9a75454..afaaf5e 100644 --- a/karma/bot.py +++ b/karma/bot.py @@ -14,6 +14,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . from typing import Awaitable, Type, Optional, Tuple +import hashlib import json import html @@ -41,6 +42,9 @@ DOWNVOTE = f"^(?:{DOWNVOTE_EMOJI}|{DOWNVOTE_EMOJI_SHORTHAND}|{DOWNVOTE_TEXT})$" class Config(BaseProxyConfig): def do_update(self, helper: ConfigUpdateHelper) -> None: helper.copy("democracy") + helper.copy("opt_out") + helper.copy("show_content") + helper.copy("store_content") helper.copy("filter") helper.copy("errors.filtered_users") helper.copy("errors.vote_on_vote") @@ -48,6 +52,10 @@ class Config(BaseProxyConfig): helper.copy("errors.already_voted") +def sha1(val: str) -> str: + return hashlib.sha1(val.encode("utf-8")).hexdigest() + + class KarmaBot(Plugin): karma_t: Type[Karma] version: Type[Version] @@ -140,15 +148,18 @@ class KarmaBot(Plugin): await evt.reply(self._karma_message_list("worst")) def _parse_content(self, evt: Event) -> str: + if not self.config["store_content"]: + return "" if isinstance(evt, MessageEvent): + evt.content.trim_reply_fallback() if evt.content.msgtype in (MessageType.NOTICE, MessageType.TEXT, MessageType.EMOTE): body = evt.content.body if evt.content.msgtype == MessageType.EMOTE: body = "/me " + body - body = body.split("\n")[0] - if len(body) > 60: - body = body[:50] + " \u2026" - body = html.escape(body) + if self.config["store_content"] == "partial": + body = body.split("\n")[0] + if len(body) > 60: + body = body[:50] + " \u2026" return body name = media_reply_fallback_body_map[evt.content.msgtype] return f"[{name}]({self.client.api.get_download_url(evt.content.url)})" @@ -169,7 +180,7 @@ class KarmaBot(Plugin): if not target: return in_filter = evt.sender in self.config["filter"] - if self.config["democracy"] == in_filter: + if self.config["democracy"] == in_filter or sha1(evt.sender) in self.config["opt_out"]: if self.config["errors.filtered_users"]: await evt.reply("Sorry, you're not allowed to vote.") return @@ -186,6 +197,9 @@ class KarmaBot(Plugin): return karma_id = dict(given_to=karma_target.sender, given_by=evt.sender, given_in=evt.room_id, given_for=karma_target.event_id) + anonymize = sha1(karma_target.sender) in self.config["opt_out"] + if anonymize: + karma_id["given_to"] = "" existing = self.karma_t.get(**karma_id) if existing is not None: if existing.value == value: @@ -195,7 +209,7 @@ class KarmaBot(Plugin): existing.update(new_value=value) else: karma = self.karma_t(**karma_id, given_from=evt.event_id, value=value, - content=self._parse_content(karma_target)) + content=self._parse_content(karma_target) if not anonymize else "") karma.insert() await evt.mark_read() @@ -203,6 +217,11 @@ class KarmaBot(Plugin): localpart, _ = self.client.parse_mxid(mxid) return "\u2063".join(localpart) + def _user_link(self, user_id: UserID) -> str: + if not user_id: + return "Anonymous" + return f"[{self._denotify(user_id)}](https://matrix.to/#/{user_id})" + def _karma_user_list(self, list_type: str) -> Optional[str]: if list_type == "top": karma_list = self.karma_t.get_top_users() @@ -213,11 +232,19 @@ class KarmaBot(Plugin): else: return None message += "\n".join( - f"{index + 1}. [{self._denotify(karma.user_id)}](https://matrix.to/#/{karma.user_id}): " + f"{index + 1}. {self._user_link(karma.user_id)}: " f"{self._sign(karma.total)} (+{karma.positive}/-{karma.negative})" - for index, karma in enumerate(karma_list)) + for index, karma in enumerate(karma_list) if karma.user_id) return message + def _message_text(self, index, event) -> str: + text = (f"{index + 1}. [Event](https://matrix.to/#/{event.room_id}/{event.event_id})" + f" by {self._user_link(event.sender)} with" + f" {self._sign(event.total)} karma (+{event.positive}/-{event.negative})\n") + if event.content and self.config["show_content"]: + text += f" \n > {html.escape(event.content)}\n" + return text + def _karma_message_list(self, list_type: str) -> Optional[str]: if list_type == "best": karma_list = self.karma_t.get_best_events() @@ -227,13 +254,8 @@ class KarmaBot(Plugin): message = "#### Worst messages\n\n" else: return None - message += "\n".join( - f"{index + 1}. [Event](https://matrix.to/#/{event.room_id}/{event.event_id})" - f" by [{self._denotify(event.sender)}](https://matrix.to/#/{event.sender}) with" - f" {self._sign(event.total)} karma (+{event.positive}/-{event.negative})\n" - f" \n" - f" > {event.content}\n" - for index, event in enumerate(karma_list)) + message += "\n".join(self._message_text(index, event) + for index, event in enumerate(karma_list)) return message @classmethod