From 5a6e5bb3c7cca1a9a6dae215de5f2bfc844bf6bb Mon Sep 17 00:00:00 2001 From: Ben <@webmaster:fiftyfiftyonearizona.org> Date: Thu, 19 Jun 2025 15:27:22 -0700 Subject: [PATCH 1/4] Terminology changes --- readme.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/readme.md b/readme.md index 84e34b3..b731f12 100644 --- a/readme.md +++ b/readme.md @@ -17,16 +17,16 @@ Every event is a text message (`m.text`). This is done so that real people can c Every message has a format similar to that of a typical UNIX-style command-line program: ``` -!report human readable message +!report human readable message ``` The bang (exclamation mark) at the beginning of the message is there so the system works with Maubot. The human readable message can be anything. Such as "ICE Spotted at 500 E Fake St." -The state/territory field is a single token (no spaces in it) which designates the state or territory that the report is for. This is a required field. This requirement and its granularity have undergone quite a bit of scrutiny. Its purpose is to allow for consumers of this information to filter by their general location without being too granular. The specific choice of state came down to most 50501 groups being organized around each state. +The region field is a single token (no spaces in it) which designates the state or territory that the report is for. This is a required field. This requirement and its granularity have undergone quite a bit of scrutiny. Its purpose is to allow for consumers of this information to filter by their general location without being too granular. The specific choice of state came down to most 50501 groups being organized around each state. -This is **not case sensitive**, but again it must be a single word/token. Just concatenate the words for state or territory names with multiple words. Like NorthDakota or puertoRico. +This is **not case sensitive**, but again it must be a single word/token. Just concatenate the words for state or territory names with multiple words. Like NorthDakota or puertoRico or NATIONAL. A more complete example might be: @@ -36,16 +36,16 @@ A more complete example might be: ## Input-Output & Authentication -This system is intended to be used in a Matrix room where different Matrix accounts are adding and consuming information. For simplicity, I will stick to the adders and consumers terminoligy. The adders can be bots or people, whereas the consumers are intended to be bots only. +This system is intended to be used in a Matrix room where different Matrix accounts are adding and consuming information. For simplicity, I will stick to the adders and consumers terminology. The adders can be bots or people, whereas the consumers are intended to be bots only. -Additionally, consumer bots would probably want to only pay attention to certain state/territory designations. Additionally, consumer bots may only want to pay attention to certain users. All Maubot plugins that FiftyFiftyOne +Additionally, consumer bots would probably want to only pay attention to certain region designations. Additionally, consumer bots may only want to pay attention to certain users. All Maubot plugins that FiftyFiftyOne As an example, a person could be the only adder, with two consumers—say a bot that forwards it to NTFY, and another one which forwards it to Signal. When the person enters in `!report arizona Some Report` the two consumer bots should process it as follows: -1. Split the message into the state/territory designator `arizona`, and the string after it. -2. If the bot is set to only pay attention to specific state/territory designators, check that `arizona` is in that set. Silently ignore the message if it isn't. -3. If the bot is set to only pay attention to specific users for the state/territory designator `arizona`, check if the sending user is in that set. Silently ignore the message if it isn't. +1. Split the message into the region designator `arizona`, and the string after it. +2. If the bot is set to only pay attention to specific region designators, check that `arizona` is in that set. Silently ignore the message if it isn't. +3. If the bot is set to only pay attention to specific users for the region designator `arizona`, check if the sending user is in that set. Silently ignore the message if it isn't. 4. Run through its own code to process the message as it sees fit. 5. Optionally reply to the message saying that it was processed. \ No newline at end of file From cd502ab4428abd559014a7a8c00e1f60b3a202f5 Mon Sep 17 00:00:00 2001 From: Ben <@webmaster:fiftyfiftyonearizona.org> Date: Tue, 24 Jun 2025 16:33:56 -0700 Subject: [PATCH 2/4] Clearer documentation and some sample code & YAML --- readme.md | 170 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 168 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index b731f12..b6989b7 100644 --- a/readme.md +++ b/readme.md @@ -26,7 +26,7 @@ The human readable message can be anything. Such as "ICE Spotted at 500 E Fake S The region field is a single token (no spaces in it) which designates the state or territory that the report is for. This is a required field. This requirement and its granularity have undergone quite a bit of scrutiny. Its purpose is to allow for consumers of this information to filter by their general location without being too granular. The specific choice of state came down to most 50501 groups being organized around each state. -This is **not case sensitive**, but again it must be a single word/token. Just concatenate the words for state or territory names with multiple words. Like NorthDakota or puertoRico or NATIONAL. +This is **not case sensitive**, but again it must be a single word/token. Just concatenate the words for state or territory names with multiple words. Like NorthDakota or puertoRico or COLORADO. A more complete example might be: @@ -34,6 +34,76 @@ A more complete example might be: !report arizona ICE seen at 500 N Central Ave in Phoenix` ``` +### Region List + +In order to be extra careful, here's an exhaustive list of all reigons for The United States, listed in alphabetical order of their proper names. + +| Name | Reigon Name | Shortcode | +| ------------------------ | ---------------------- | --------- | +| Nationwide | __global__ | AL | +| Alabama | Alabama | AL | +| Alaska | Alaska | AK | +| American Samoa | AmericanSamoa | AS | +| Arizona | Arizona | AZ | +| Arkansas | Arkansas | AR | +| California | California | CA | +| Colorado | Colorado | CO | +| Connecticut | Connecticut | CT | +| Delaware | Delaware | DE | +| District of Columbia | WashingtonDC | DC | +| Florida | Florida | FL | +| Georgia | Georgia | GA | +| Guam | Guam | GU | +| Hawaii | Hawaii | HI | +| Idaho | Idaho | ID | +| Illinois | Illinois | IL | +| Indiana | Indiana | IN | +| Iowa | Iowa | IA | +| Kansas | Kansas | KS | +| Kentucky | Kentucky | KY | +| Louisiana | Louisiana | LA | +| Maine | Maine | ME | +| Maryland | Maryland | MD | +| Massachusetts | Massachusetts | MA | +| Michigan | Michigan | MI | +| Minnesota | Minnesota | MN | +| Mississippi | Mississippi | MS | +| Missouri | Missouri | MO | +| Montana | Montana | MT | +| Nebraska | Nebraska | NE | +| Nevada | Nevada | NV | +| New Hampshire | NewHampshire | NH | +| New Jersey | NewJersey | NJ | +| New Mexico | NewMexico | NM | +| New York | NewYork | NY | +| North Carolina | NorthCarolina | NC | +| North Dakota | NorthDakota | ND | +| Northern Mariana Islands | NorthernMarianaIslands | MP | +| Ohio | Ohio | OH | +| Oklahoma | Oklahoma | OK | +| Oregon | Oregon | OR | +| Pennsylvania | Pennsylvania | PA | +| Puerto Rico | PuertoRico | PR | +| Rhode Island | RhodeIsland | RI | +| South Carolina | SouthCarolina | SC | +| South Dakota | SouthDakota | SD | +| Tennessee | Tennessee | TN | +| Texas | Texas | TX | +| Utah | Utah | UT | +| U.S. Virgin Islands | USVirginIslands | VI | +| Vermont | Vermont | VT | +| Virginia | Virginia | VA | +| Washington | Washington | WA | +| West Virginia | WestVirginia | WV | +| Wisconsin | Wisconsin | WI | +| Wyoming | Wyoming | WY | + +And here's a convenient one-line list of the shortcodes: + +``` +all al ak as az ar ca co ct de dc fl ga gu hi id il in ia ks ky la me md ma mi mn ms mo mt ne nv nh nj nm ny nc nd mp oh ok or pa pr ri sc sd tn tx ut vi vt va wa wv wi wy +``` + ## Input-Output & Authentication This system is intended to be used in a Matrix room where different Matrix accounts are adding and consuming information. For simplicity, I will stick to the adders and consumers terminology. The adders can be bots or people, whereas the consumers are intended to be bots only. @@ -48,4 +118,100 @@ When the person enters in `!report arizona Some Report` the two consumer bots sh 2. If the bot is set to only pay attention to specific region designators, check that `arizona` is in that set. Silently ignore the message if it isn't. 3. If the bot is set to only pay attention to specific users for the region designator `arizona`, check if the sending user is in that set. Silently ignore the message if it isn't. 4. Run through its own code to process the message as it sees fit. -5. Optionally reply to the message saying that it was processed. \ No newline at end of file +5. Optionally reply or react to the message saying that it was processed. + +## Maubot Specifics + +Here's some sample code and configuration layout to make plugin development easier. + +## Sample Configuration + +```yaml +# Which regions the bot should pay attention to +# This can be either an empty list to allow any sender for this reigon, +# or can have a specified whitelist of senders +allowed_regions: + foo: + - "@reports:fiftyfiftyonerarizona.org" + bar: + - "@reports:example.org" + - "@reports:example.com" + buzz: + aaa: +``` + +```yaml +# Bot-specific configurations per region +# The fields in each specific reigon should be the same, but their values should differ +# In this case (with NTFY), the bot should post notifications for each reigon +# to a different topic. +region_configs: + __global__: + # Where and how the plugin will send the data to + server_url: "https://ntfy.sh" + server_topic: "changeme2" + + # If the plugin should use a username and password to send notifications + server_use_authentication: true + server_username: "no" + server_password: "way" + foo: + # Where and how the plugin will send the data to + server_url: "https://ntfy.sh" + server_topic: "changeme" + + # If the plugin should use a username and password to send notifications + server_use_authentication: true + server_username: "no" + server_password: "way" + bar: + # Where and how the plugin will send the data to + server_url: "https://ntfy.sh" + server_topic: "changeme2" + + # If the plugin should use a username and password to send notifications + server_use_authentication: true + server_username: "no" + server_password: "way" +``` + +## Helper Functions + +```python +# Checks if a sender if allowed to send for a particular region +def validateSender(self, region: str, sender: str): + # Check that config value exists + if not self.config["allowed_regions"]: return False + # Check that region is allowed + if not self.config["allowed_regions"][region]: return False + # All senders allowed for this region + if len(self.config["allowed_regions"][region]) == 0: return True + # Check that sender is allowed for region + if not sender in self.config["allowed_regions"][region]: return False + return True +``` + +```python +# Does the necesary config checks for the given event +# Returns list of regions to process (strings) +# Currently just the specified region and "__global__" +def validateReport(self, evt: MessageEvent, message: str): + # Split command (minus !command_name) into tokens + tokens = message.split() + region = tokens[0].lower() + + # Each command must have a state/territory designation and a message + if len(tokens) < 2: return None + + self.log.debug(region) + + # This is a list of regions to process for this specific message + # This is only used to consider __global__ + regions_to_process = [] + if (self.validateSender("__global__", evt.sender)): + regions_to_process.append("__global__") + if (self.validateSender(region, evt.sender)): + regions_to_process.append(region) + + return regions_to_process +``` From e0bd55088ef002bcc58d7078af94b55874fcfe8e Mon Sep 17 00:00:00 2001 From: 50501AZ Webmaster Date: Thu, 26 Jun 2025 16:43:51 -0700 Subject: [PATCH 3/4] Minor fix --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index b6989b7..d58cd3c 100644 --- a/readme.md +++ b/readme.md @@ -183,7 +183,7 @@ def validateSender(self, region: str, sender: str): # Check that config value exists if not self.config["allowed_regions"]: return False # Check that region is allowed - if not self.config["allowed_regions"][region]: return False + if not region in self.config["allowed_regions"]: return False # All senders allowed for this region if len(self.config["allowed_regions"][region]) == 0: return True # Check that sender is allowed for region From c481650a4a3cadf3981e02abcb413b1c1c75b26e Mon Sep 17 00:00:00 2001 From: 50501AZ Webmaster Date: Mon, 30 Jun 2025 17:50:59 -0700 Subject: [PATCH 4/4] Heavy face-lift for version 1.2 --- readme.md | 144 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 88 insertions(+), 56 deletions(-) diff --git a/readme.md b/readme.md index d58cd3c..ebcd531 100644 --- a/readme.md +++ b/readme.md @@ -40,7 +40,7 @@ In order to be extra careful, here's an exhaustive list of all reigons for The U | Name | Reigon Name | Shortcode | | ------------------------ | ---------------------- | --------- | -| Nationwide | __global__ | AL | +| Nationwide | __global__ | ALL | | Alabama | Alabama | AL | | Alaska | Alaska | AK | | American Samoa | AmericanSamoa | AS | @@ -122,14 +122,18 @@ When the person enters in `!report arizona Some Report` the two consumer bots sh ## Maubot Specifics -Here's some sample code and configuration layout to make plugin development easier. +Here's some sample code and configuration layout to make plugin development easier. You can copy paste these into your plugin, or use it as inspiration. FiftyFiftyOneArizona's plugins try to be as consistent as possible with its configuration options, and how the plugin behaves with that configuration. ## Sample Configuration +Which users are allowed to send reports for which regions: + ```yaml -# Which regions the bot should pay attention to -# This can be either an empty list to allow any sender for this reigon, -# or can have a specified whitelist of senders +# Which regions the bot should pay attention to. +# Pass through empty list to allow all senders for a region. +# Add a list of MatrixIDs to whitelist senders for a region. +# Use __global__ to refer to all regions. +# Code looks to see if a sender is allowed for a specific region OR __global__. allowed_regions: foo: - "@reports:fiftyfiftyonerarizona.org" @@ -138,80 +142,108 @@ allowed_regions: - "@reports:example.com" buzz: aaa: + ``` +Different configurations for each regions, and one which processes reports for all regions: + ```yaml # Bot-specific configurations per region -# The fields in each specific reigon should be the same, but their values should differ -# In this case (with NTFY), the bot should post notifications for each reigon -# to a different topic. +# Code will assume no authentication if either username or password are missing. +# Use __global__ to specify an endpoint for all regions. +# Code will push to both __global__ and region-specific endpoint. region_configs: - __global__: - # Where and how the plugin will send the data to - server_url: "https://ntfy.sh" - server_topic: "changeme2" - - # If the plugin should use a username and password to send notifications - server_use_authentication: true - server_username: "no" - server_password: "way" - foo: + arizona: # Where and how the plugin will send the data to server_url: "https://ntfy.sh" server_topic: "changeme" # If the plugin should use a username and password to send notifications - server_use_authentication: true - server_username: "no" - server_password: "way" - bar: - # Where and how the plugin will send the data to - server_url: "https://ntfy.sh" - server_topic: "changeme2" + username: "no" + password: "way" +``` - # If the plugin should use a username and password to send notifications - server_use_authentication: true - server_username: "no" - server_password: "way" +Defining which rooms a producer should send to for each region, and for all regions: + +```yaml +sendto: + __global__: + - "!example:example.com" + arizona: + - "!example:example.com" + - "!example2:example.com" + california: + - "!example:example.com" ``` ## Helper Functions -```python -# Checks if a sender if allowed to send for a particular region -def validateSender(self, region: str, sender: str): - # Check that config value exists - if not self.config["allowed_regions"]: return False - # Check that region is allowed - if not region in self.config["allowed_regions"]: return False - # All senders allowed for this region - if len(self.config["allowed_regions"][region]) == 0: return True - # Check that sender is allowed for region - if not sender in self.config["allowed_regions"][region]: return False - return True -``` +Handling which users are allowed to send reports for which regions. Takes in region string and sender MatrixID as a string. Returns a boolean. ```python -# Does the necesary config checks for the given event -# Returns list of regions to process (strings) -# Currently just the specified region and "__global__" -def validateReport(self, evt: MessageEvent, message: str): +# Checks if a sender if allowed to send for a particular region +def validate_sender(self, region: str, sender: str): + # Mautrix isn't documented, like at all, so I'm just gonna catch the + # error because IDK how to see if a map is inside a map. + try: allowed_list = self.config["allowed_regions"][region] + except: return False + + if allowed_list is None: return True # Empty list + if sender in allowed_list: return True + + # Sender not allowed in region config + return False +``` + +Handling different configurations for each regions. Parses an event and its body, returns a list of configurations. List can be empty. Depends on `validate_sender` function above. + +This function also handles the following edge cases + +1. Sender allowed globally but `__global__` is not defined in the region configs. +2. Sender allowed for a region, and both that region and `__global__` is defined. +3. Sender is allowed either globally or for a region, but neither that region or `__global__` are defined. An empty list will be returned. + + +```python +# Does the necessary config checks for the given event +# Returns list of recursive configs to process +def validate_report(self, evt: MessageEvent, message: str): # Split command (minus !command_name) into tokens tokens = message.split() region = tokens[0].lower() # Each command must have a state/territory designation and a message - if len(tokens) < 2: return None + if len(tokens) < 2: return [] + # And we must have self.config["region_configs"] + try: trashvariable = self.config["region_configs"] + except: return [] - self.log.debug(region) + configs = [] # To be returned - # This is a list of regions to process for this specific message - # This is only used to consider __global__ - regions_to_process = [] - if (self.validateSender("__global__", evt.sender)): - regions_to_process.append("__global__") - if (self.validateSender(region, evt.sender)): - regions_to_process.append(region) + allowed_globally = self.validate_sender("__global__", evt.sender) + allowed_region = self.validate_sender(region, evt.sender) - return regions_to_process + # If user is allowed globally and/or for a region, + # the plugin should process both the region and __global__ configs + if allowed_globally or allowed_region: + for i in ["__global__", region]: + try: configs.append(self.config["region_configs"][i]) + except: trashvariable = None + + return configs +``` + +Getting a set of rooms to send to, inclusive of `__global__` and the region name. + +```python +def get_send_rooms(self, region_name): + room_ids = set() + + if "sendto" in self.config: return room_ids + for i in [region_name, "__global__"]: + if i in self.config["sendto"]: + for room_id in self.config["sendto"][i]: + room_ids.add(room_id) + + return room_ids ```