{ "policy_kind": "Trigger", "action_types": [ "expensecreated" ], "name": "Vote on OpenCollective expenses in Slack with restricted voting ", "description": "Posts OpenCollective expenses to Slack channel to be voted on. If expense has three or more yes votes and one or less no votes after three hours, approves, otherwise waits until three days has passed and approves if there are any yes votes and no no votes, otherwise rejects. Once the vote is resolved, posts to both Slack thread and the OpenCollective expense thread with vote results. Restricts voting on OpenCollective expenses to eligible voters.", "is_bundled": false, "filter": "return True\n\n", "initialize": "pass", "check": "if not proposal.vote_post_id:\n return None #the vote hasn't started yet\n\n# Check the status of the vote and evaluate a closing condition.\n\nyes_votes = proposal.get_yes_votes().count()\nno_votes = proposal.get_no_votes().count()\n#logger.debug(f\"{yes_votes} for, {no_votes} against\")\n\ntime_elapsed = proposal.get_time_elapsed()\n\n# we never close a vote before three hours\nif time_elapsed < datetime.timedelta(hours=3):\n return None \n\n# if there are three or more votes for, and one or less against, it passes\nif yes_votes >= 2 and no_votes <= 1:\n return PASSED\n\n# if there's more than one vote against, and less than three yes votes, it fails\nif no_votes > 0:\n return FAILED\n\n# if it's been three days, and there are any yeses and no nos, pass, otherwise fail\nif time_elapsed > datetime.timedelta(days=3):\n logger.info(\"Ending based on time\")\n reached_threshold = yes_votes > 0 and no_votes == 0\n return PASSED if reached_threshold else FAILED\n\nreturn PROPOSED # still pending", "notify": "# Start a vote on Slack. Only 2 users are eligible to vote.\ndiscussion_channel = \"C0177HZTXXX\"\n\neligible_voters = [\"UABC123\", \"UABC999\"] # Slack member IDs for Alice and Bob\nmessage = f\"Vote on whether to approve <{action.url}|this request> for funds: {action.description}\"\nslack.initiate_vote(text=message, channel=discussion_channel, users=eligible_voters)\n\n# Start a discussion thread on the voting message\nslack.post_message(text=\"Discuss here! :meow-wave:\", channel=discussion_channel, thread_ts=proposal.vote_post_id)\n\n# Add a comment to the expense on Open Collective with a link to the Slack vote\nlink = f\"on Slack\"\ntext = f\"Thank you for submitting a request! A vote has been started {link}.\"\nopencollective.post_message(text=text, expense_id=action.expense_id)\n", "success": "# approve the expense\nopencollective.process_expense(action=\"APPROVE\", expense_id=action.expense_id)\n\nyes_votes = proposal.get_yes_votes().count()\nno_votes = proposal.get_no_votes().count()\nmessage = f\"Expense approved. The vote passed with {yes_votes} for and {no_votes} against.\"\n\n# comment on the expense\nopencollective.post_message(text=message, expense_id=action.expense_id)\n\n# update the Slack thread\ndiscussion_channel = \"C0177HZTXXX\"\nslack.post_message(text=message, channel=discussion_channel, thread_ts=proposal.vote_post_id)\n", "fail": "# reject the expense\nopencollective.process_expense(action=\"REJECT\", expense_id=action.expense_id)\n\nyes_votes = proposal.get_yes_votes().count()\nno_votes = proposal.get_no_votes().count()\nmessage = f\"Expense rejected. The vote failed with {yes_votes} for and {no_votes} against.\"\n\n# comment on the expense\nopencollective.post_message(text=message, expense_id=action.expense_id)\n\n# update the Slack thread\ndiscussion_channel = \"C0177HZTXXX\"\nslack.post_message(text=message, channel=discussion_channel, thread_ts=proposal.vote_post_id)\n" }