5 Commits

39 changed files with 704 additions and 554 deletions

6
.gitignore vendored
View File

@ -1,5 +1 @@
*/data/* */data
!*/data/placeholder
# ignore Mastodon credentials
modpol_mastodon/creds/

View File

@ -1,43 +1,39 @@
# Modpol # Modpol for Minetest
![](lib/empire-modpol.png)
Modpol, short for "modular politics," enables diverse governance processes on multi-user platforms. It offers a library with which users can choose, modify, and create modules that add specific governance functionalities. Modpol, short for "modular politics," enables diverse governance processes on multi-user platforms. It offers a library with which users can choose, modify, and create modules that add specific governance functionalities.
**Learn more at [modpol.net](https://modpol.net).** This implementation is a mod for [Minetest](https://minetest.net), a free/open-source voxel game. It is designed to be adapted to other multi-user platforms that also employ Lua as an extension language.
## Modpol for Minetest ## How to use it
This implementation is a mod for [Minetest](https://minetest.net), a free/open-source voxel game. To use in Minetest, simply install the full codebase in your `mods/` or `worldmods/` folder. Minetest will load `init.lua`. Modpol is built around groups called *orgs*. At the base is an org with all users in it, called `Root` by default. *Modules* enable people to do things within orgs, such as decide on membership, grant powers to the org, and much more. To get started in Minetest:
* Type the command `/mp`
* Select the org `Root`
* Choose one of its modules to make new orgs and craft their behavior
![](lib/module_list.png)
Modules can be nested in each other, so one module can rely on another module to accomplish a process. Users might use a module to unilaterally carry out actions in the game, or the module might require a group decision to do so. Users can also change the modules available to users of a given org. There are currently two ways of doing this:
* Admins can remove modules from the list of modules loaded in `modpol_core/api.lua` and `modpol_minetest/api.lua`. This will make those modules no longer available to any user.
* Players can change the modules available in a given org from within the program using the `Change modules` module. Removed modules can be re-added in any org by using `Change modules` again.
Modpol should give you the ability to do whatever kind of politics you want with your modules. If there is something you would like to do that is not available, [develop a module for it](https://gitlab.com/medlabboulder/modpol/-/wikis/Module-Writing-Guide) (or ask us for help!).
## Installation in Minetest
To use this in Minetest, simply install it in your `mods/` or `worldmods/` folder. Minetest will load `init.lua`.
In the game, open the Modpol dashboard with the command `/mp`. In the game, open the Modpol dashboard with the command `/mp`.
For testing purposes, players with the `privs` privilege (generally admins) can use the `/mptest` command, which resets all the orgs and opens a dashboard. For testing purposes, players with the `privs` privilege (generally admins) can use the `/mp` command, which resets all the orgs and opens a dashboard.\
## Mastodon Govbot
Modpol can operate a governance-supporting bot for communities on [Mastodon](https://joinmastodon.org), an open social network. ## Standalone Version on the Command Line
This version utilizes and requires the installation of [lupa](https://github.com/scoder/lupa), which integrates Lua into Python, and [Mastodon.py](https://pypi.org/project/Mastodon.py/), a Python library for interacting with the Mastodon API. Modpol can also be used independently of Minetest as a command-line tool. Currently command-line use of modpol requires a Unix-style system, but it is intended to become more fully platform independent.
*To do: explain how to set up the bot on the server.*
* Install Python on your system and the two required libraries:
- `pip install lupa`
- `pip install Mastodon.py`
* Set up files `clientcred.secret` and `usercred.secret` in the `modpol_mastodon/creds/` directory, using the account settings in Mastodon
Once it is configured, to initialize the bot, run from Modpol's base directory:
```
python govbot.py
```
This in turn calls login-mastodon.py, which initializes Modpol and the bot.
## Command Line
Modpol can also be used as a command-line tool. Currently command-line use of modpol requires a Unix-style system, but it is intended to become more fully platform independent.
The command-line version is in the `modpol` subdirectory. To run the program on Unix systems in CLI mode, install lua or luajit and execute the following in this directory: The command-line version is in the `modpol` subdirectory. To run the program on Unix systems in CLI mode, install lua or luajit and execute the following in this directory:
@ -56,12 +52,25 @@ In the interpreter, for a list of global functions and tables, use `modpol.menu(
## Storage ## Storage
The persistent storage method may be chosen in `modpol.lua`. The persistent storage method may be chosen in `modpol.lua`. If available, Modpol uses Minetest's built-in StorageRef system for Minetest 5.*. If that is not available, or in CLI mode, data will be stored in a data directory at `modpol_core/data/`. This will contain a log file and serialized program data files.
By default, data will be stored in a data directory at `modpol_core/data/`. This will contain a log file and serialized program data files.
For Minetest: If available, Modpol uses Minetest's built-in StorageRef system for Minetest 5.*. ## Design philosophy
Modpol seeks to implement a theoretical framework called "[modular politics](https://metagov.org/modpol)," which proposes these design goals:
* *Modularity*: Platform operators and community members should have the ability to construct systems by creating, importing, and arranging composable parts together as a coherent whole.
* *Expressiveness*: The governance layer should be able to implement as wide a range of processes as possible.
* *Portability*: Governance tools developed for one platform should be portable to another platform for reuse and adaptation.
* *Interoperability*: Governance systems operating on different platforms and protocols should have the ability to interact with each other, sharing data and influencing each other's processes.
Additionally, Modpol seeks to counteract the tendency for "[implicit feudalism](https://ntnsndr.in/ImplicitFeudalism)," according to which rigid, top-down power structures are the norm in online spaces. To this end, some design patterns include:
* *Groups, not roles*: While most platforms assign powers through particular permissions given to individuals, in Modpol, power lies in groups (which Modpol calls "orgs").
* *Consent, not oligarchy*: Rather than assuming that decisions will be made by a few power-holders, the software assumes that consent by all affected users is the norm.
* *Inheritance, not blank slates*: When a new group is formed, it inherits the patterns of what preceded it, rather than imagining that it is starting from scratch.
It is certainly possible to use Modpol to replicate practices of implicit feudalism, such as all-powerful admins, but doing so requires extra work to overcome these defaults.
## Documentation ## Documentation
@ -92,7 +101,7 @@ We are grateful for initial support for this project from a residency with [The
## Contributing ## Contributing
We'd love to welcome more contributors. Please join the conversation in the [Issues](https://gitlab.com/medlabboulder/modpol/-/issues), the \#modpol channel at the [Metagovernance Project](https://metagov.org) Slack, and the [Minetest.net forum](https://forum.minetest.net/viewtopic.php?f=47&t=26037). We'd love to welcome more contributors. Please join the conversation in the [Issues](https://gitlab.com/medlabboulder/modpol/-/issues), our [Matrix.org channel](https://matrix.to/#/#minetest-modpol:matrix.org), and the [Minetest.net forum](https://forum.minetest.net/viewtopic.php?f=47&t=26037).
Learn more about the project and how to develop your own modules in [the wiki](https://gitlab.com/medlabboulder/modpol/-/wikis/home). Learn more about the project and how to develop your own modules in [the wiki](https://gitlab.com/medlabboulder/modpol/-/wikis/home).

113
govbot.py
View File

@ -1,113 +0,0 @@
# MODPOL GOVBOT
# A Python bridge between Modpol and Mastodon
## Initialize Modpol ##
import lupa
# Create a Lua runtime
lua = lupa.LuaRuntime(unpack_returned_tuples=True)
# Load your Lua script
script_path = 'login_mastodon.lua'
with open(script_path, 'r') as f:
lua.execute(f.read())
print("Loaded Modpol into Govbot")
# To do: load all override files
# If your Lua script defines functions, you can call them from Python
#result = lua.globals().your_function_name() # Replace with your actual function name
#print("Result from Lua:", result)
## Initialize Mastodon ##
from mastodon import Mastodon
# ^ https://mastodonpy.readthedocs.io/en/stable/#
import time
import datetime
# ^ https://github.com/HikaruSama233/mastodon_autoreply_bot_template/blob/main/bot_template.py
# Variables
## Character maximum for the server
char_max = 500
# Connect to Mastodon
govbot = Mastodon(
client_id='modpol_mastodon/creds/clientcred.secret',
access_token = 'modpol_mastodon/creds/usercred.secret')
print("Signed in to Mastodon")
# Handle notification events
def handle_event(event):
print("{} Handling notification ".format(datetime.datetime.now())
+ str(event['id']))
if 'mention' in event['type']:
mention_username = event['account']['username']
print(str(event['id']) + ": mention from @"
+ mention_username)
# Command handling
# perhaps move this to a separate Interfaces file
if "!dashboard" in event['status']['content']:
govbot.status_reply(event['status'],
"[dashboard here]")
print("{} Replied to ".format(datetime.datetime.now())
+ str(event['id']))
# To do: if DM
else:
govbot.status_reply(event['status'],
"Thanks for mentioning me!")
print("{} Replied to ".format(datetime.datetime.now())
+ str(event['id']))
## ADD OTHER IF STATEMENTS HERE
else:
govbot.status_reply(event['status'],
"I'm not sure how to parse that.")
print("{} Posted error message ".format(datetime.datetime.now())
+ str(event['id']))
# Check notifications
events_queue = {}
def check_events():
notifications = govbot.notifications(mentions_only=True)
current_time = datetime.datetime.now()
if len(notifications) > 0:
print("{} Total notifications {}".format(current_time, len(notifications)))
events_queue = notifications
while len(events_queue) > 0:
id = events_queue[0]['id']
handle_event(events_queue.pop(0)) # removes event from queue
govbot.notifications_dismiss(id)
elif str(current_time.minute) == '0':
print("{} No notifications".format(current_time))
# Initialization
# Delete past posts (while we are testing)
## Get account information
account_info = govbot.account_verify_credentials()
## Extract and print account ID
acc_id = account_info['id']
## Fetch the bot's posts
old_posts = govbot.account_statuses(id=acc_id)
## Delete
for post in old_posts:
govbot.status_delete(id=post.id)
# Delete past notifications
govbot.notifications_clear()
print("{} Initialized".format(datetime.datetime.now()))
first_post = "Commands:"
first_post += "\n!dashboard: View dashboard"
govbot.status_post(first_post[:char_max])
while 1:
# Check every 5 seconds
time.sleep(5)
if len(events_queue) == 0:
check_events()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 588 KiB

View File

@ -1,2 +0,0 @@
dofile("modpol_core/modpol.lua")

View File

@ -12,22 +12,27 @@ dofile (localdir .. "/interactions/interactions.lua")
--modules --modules
--TODO make this automatic and directory-based --TODO make this automatic and directory-based
dofile (localdir .. "/modules/add_child_org.lua") dofile (localdir .. "/storage/store-modules.lua")
dofile (localdir .. "/modules/change_modules.lua") modpol.load_modules(localdir .. "/modules")
dofile (localdir .. "/modules/change_policy.lua")
dofile (localdir .. "/modules/consent.lua") -- dofile (localdir .. "/modules/add_child_org_consent.lua")
dofile (localdir .. "/modules/create_token.lua") -- dofile (localdir .. "/modules/add_child_org.lua")
dofile (localdir .. "/modules/defer.lua") -- dofile (localdir .. "/modules/change_modules.lua")
dofile (localdir .. "/modules/display_policies.lua") -- dofile (localdir .. "/modules/change_policy.lua")
dofile (localdir .. "/modules/display_processes.lua") -- dofile (localdir .. "/modules/consent.lua")
dofile (localdir .. "/modules/join_org.lua") -- dofile (localdir .. "/modules/create_token.lua")
dofile (localdir .. "/modules/leave_org.lua") -- dofile (localdir .. "/modules/defer_consent.lua")
dofile (localdir .. "/modules/message_org.lua") -- dofile (localdir .. "/modules/display_policies.lua")
dofile (localdir .. "/modules/randomizer.lua") -- dofile (localdir .. "/modules/display_processes.lua")
dofile (localdir .. "/modules/remove_child_org.lua") -- dofile (localdir .. "/modules/join_org_consent.lua")
dofile (localdir .. "/modules/remove_member.lua") -- dofile (localdir .. "/modules/leave_org.lua")
dofile (localdir .. "/modules/remove_org.lua") -- dofile (localdir .. "/modules/message_org.lua")
dofile (localdir .. "/modules/remove_process.lua") -- dofile (localdir .. "/modules/randomizer.lua")
dofile (localdir .. "/modules/rename_org.lua") -- dofile (localdir .. "/modules/remove_child_consent.lua")
dofile (localdir .. "/modules/send_token.lua") -- dofile (localdir .. "/modules/remove_member_consent.lua")
dofile (localdir .. "/modules/tokenomics.lua") -- dofile (localdir .. "/modules/remove_org_consent.lua")
-- dofile (localdir .. "/modules/remove_org.lua")
-- dofile (localdir .. "/modules/remove_process.lua")
-- dofile (localdir .. "/modules/rename_org_consent.lua")
-- dofile (localdir .. "/modules/send_token.lua")
-- dofile (localdir .. "/modules/tokenomics.lua")

View File

@ -27,11 +27,7 @@ function modpol.interactions.get_policy_string(
this_policy = this_policy =
tostring(this_org.policies[module_slug][k]) tostring(this_org.policies[module_slug][k])
else else
if not v then this_policy = tostring(v)
this_policy = "none"
else
this_policy = tostring(v)
end
end end
table.insert(output, k.." - "..this_policy) table.insert(output, k.." - "..this_policy)
end end
@ -123,7 +119,7 @@ function modpol.interactions.dashboard(user)
print("Orgs and users reset") print("Orgs and users reset")
modpol.interactions.dashboard(user) modpol.interactions.dashboard(user)
elseif sel == "Q" or sel == "q" then elseif sel == "Q" or "q" then
return return
else else
@ -152,9 +148,7 @@ function modpol.interactions.org_dashboard(user, org_string)
local children = {} local children = {}
for k,v in ipairs(org.children) do for k,v in ipairs(org.children) do
local this_child = modpol.orgs.get_org(v) local this_child = modpol.orgs.get_org(v)
if this_child then table.insert(children, this_child.name)
table.insert(children, this_child.name)
end
end end
-- prepare modules menu -- prepare modules menu
@ -171,13 +165,7 @@ function modpol.interactions.org_dashboard(user, org_string)
table.sort(modules) table.sort(modules)
-- list pending -- list pending
local processes = {} local process_msg = #org.processes .. " total processes"
for i,v in ipairs(org.processes) do
if v ~= "deleted" then
processes[i] = org.processes[i]
end
end
local process_msg = #processes .. " total processes"
if org.pending[user] then if org.pending[user] then
process_msg = process_msg .. " (" .. process_msg = process_msg .. " (" ..
modpol.util.num_pairs(org.pending[user]) .. " pending)" modpol.util.num_pairs(org.pending[user]) .. " pending)"
@ -219,7 +207,6 @@ function modpol.interactions.org_dashboard(user, org_string)
org.name, module.slug, "\n").. org.name, module.slug, "\n")..
"\n".."Proceed?", "\n".."Proceed?",
function(input) function(input)
print("\n")
if input == "Yes" then if input == "Yes" then
org:call_module(module_sel, user) org:call_module(module_sel, user)
elseif input == "No" then elseif input == "No" then
@ -231,17 +218,20 @@ function modpol.interactions.org_dashboard(user, org_string)
print("Error: Module not found.") print("Error: Module not found.")
modpol.interactions.org_dashboard(user, org.id) modpol.interactions.org_dashboard(user, org.id)
end end
elseif sel == 'p' or sel == 'P' then -- Pending processes elseif sel == 'p' or sel == 'P' then
local processes = {}
print("All processes: (* indicates pending)") print("All processes: (* indicates pending)")
for i,v in ipairs(processes) do for i,v in ipairs(org.processes) do
local active = '' if v ~= "deleted" then
if org.pending[user] then local active = ''
if org.pending[user][v.id] then if org.pending[user] then
active = '*' if org.pending[user][v.id] then
active = '*'
end
end end
print("["..v.id.."] "..v.slug..active)
end end
print("["..v.id.."] "..v.slug..active)
end end
print() print()
print("Interact with which one (use [id] number)?") print("Interact with which one (use [id] number)?")
@ -257,7 +247,6 @@ function modpol.interactions.org_dashboard(user, org_string)
if org.pending[user][process.id] then if org.pending[user][process.id] then
org:interact(process.id, user) org:interact(process.id, user)
end end
modpol.interactions.org_dashboard(user, org.id)
end end
elseif sel == 'b' or sel == 'B' then elseif sel == 'b' or sel == 'B' then
modpol.interactions.dashboard(user) modpol.interactions.dashboard(user)
@ -348,14 +337,14 @@ function modpol.interactions.display(user, title, message, completion)
modpol.interactions.message( modpol.interactions.message(
self.initiator, "Error: input not typed for display") self.initiator, "Error: input not typed for display")
if completion then completion() else if completion then completion() else
modpol.interactions.dashboard(user) modpol.intereactions.dashboard(user)
end end
end end
print(message) print(message)
print("\nEnter to continue") print("\nEnter to continue")
io.read() io.read()
if completion then completion() else if completion then completion() else
modpol.interactions.dashboard(user) modpol.intereactions.dashboard(user)
end end
end end
@ -478,8 +467,10 @@ function modpol.interactions.binary_poll_user(user, question, func)
answer = io.read() answer = io.read()
until answer == "y" or answer == "n" until answer == "y" or answer == "n"
if answer == "y" then if answer == "y" then
modpol.interactions.message(user, "Response recorded")
func("Yes") func("Yes")
elseif answer == "n" then elseif answer == "n" then
modpol.interactions.message(user, "Response recorded")
func("No") func("No")
else else
modpol.interactions.message(user, "Error: invalid response") modpol.interactions.message(user, "Error: invalid response")

View File

@ -55,7 +55,7 @@ dofile (topdir .. "/util/misc.lua")
-- Select a storage method -- Select a storage method
-- -- preferably, declare this in the init.lua that calls modpol.lua This is the default. -- -- preferably, declare this in the init.lua that calls modpol.lua This is the default.
-- Works with CLI: -- Works with CLI:
modpol.storage_file_path = modpol.storage_file_path or topdir .. "/storage/storage-local.lua" modpol.storage_file_path = modpol.storage_file_path or topdir .. "/storage/unix-storage.lua"
-- Works with Minetest 5: -- Works with Minetest 5:
--modpol.storage_file_path = modpol.storage_file_path or topdir .. "/storage/storage-mod_storage.lua") --modpol.storage_file_path = modpol.storage_file_path or topdir .. "/storage/storage-mod_storage.lua")

View File

@ -1,4 +1,4 @@
--- Adds a child org --- Adds a child org.
-- @module add_child_org -- @module add_child_org
local add_child_org = { local add_child_org = {
@ -8,10 +8,9 @@ local add_child_org = {
} }
add_child_org.data = { add_child_org.data = {
child_name = "", child_name = "",
result = false result = nil
} }
add_child_org.config = { add_child_org.config = {
approval_module = false
} }
--- Initiate consent for new child org --- Initiate consent for new child org
@ -43,35 +42,23 @@ function add_child_org:initiate(result)
modpol.interactions.message( modpol.interactions.message(
self.initiator, self.initiator,
"Proposed child org: " .. input) "Proposed child org: " .. input)
-- initiate consent process -- create the org
self.data.result = result self:create_child_org()
self:call_module(
self.config.approval_module,
self.initiator,
{
prompt = "Create child org " ..
self.data.child_name .. "?",
votes_required = #self.org.members
},
function()
self:create_child_org()
end
)
modpol.interactions.org_dashboard( modpol.interactions.org_dashboard(
self.initiator, self.org.name) self.initiator, self.org.name)
end end
) )
end end
--- Create a new child org --- Create a new child orgg
-- @function add_child_org:create_child_org -- @function add_child_org:create_child_org
function add_child_org:create_child_org() function add_child_org:create_child_org()
self.org:add_org(self.data.child_name, self.initiator) self.org:add_org(self.data.child_name, self.initiator)
modpol.interactions.message_org( modpol.interactions.message_org(
self.initiator, self.initiator,
self.org.name, self.org.name,
"Created child org in " .. self.org.name .. ": " "Created child org "
.. self.data.child_name) ..self.data.child_name)
if self.data.result then self.data.result() end if self.data.result then self.data.result() end
self.org:delete_process(self.id) self.org:delete_process(self.id)
end end

View File

@ -0,0 +1,78 @@
--- Adds a child org.
-- Depends on `consent`
-- @module add_child_org_consent
local add_child_org_consent = {
name = "Add child org (consent)",
slug = "add_child_org_consent",
desc = "Create a child org in the current one with member consent"
}
add_child_org_consent.data = {
child_name = "",
result = nil
}
add_child_org_consent.config = {
}
--- Initiate consent for new child org
-- @function add_child_org_consent:initiate(result)
-- @param result Callback if this module is embedded in other modules
function add_child_org_consent:initiate(result)
modpol.interactions.text_query(
self.initiator,"Child org name: ",
function(input)
if input == "" then
modpol.interactions.message(
self.initiator,
"No name entered for child org")
modpol.interactions.org_dashboard(
self.initiator, self.org.name)
self.org:delete_process(self.id)
return
elseif modpol.orgs.get_org(input) then
modpol.interactions.message(
self.initiator,
"Org name already in use")
modpol.interactions.org_dashboard(
self.initiator, self.org.name)
self.org:delete_process(self.id)
if result then result() end
return
end
self.data.child_name = input
modpol.interactions.message(
self.initiator,
"Proposed child org: " .. input)
-- initiate consent process
self:call_module(
"consent",
self.initiator,
{
prompt = "Create child org " ..
self.data.child_name .. "?",
votes_required = #self.org.members
},
function()
self:create_child_org()
end
)
modpol.interactions.org_dashboard(
self.initiator, self.org.name)
end
)
end
--- Create a new child orgg
-- @function add_child_org_consent:create_child_org
function add_child_org_consent:create_child_org()
self.org:add_org(self.data.child_name, self.initiator)
modpol.interactions.message_org(
self.initiator,
self.org.name,
"Consent reached: created child org "
..self.data.child_name)
if self.data.result then self.data.result() end
self.org:delete_process(self.id)
end
modpol.modules.add_child_org_consent = add_child_org_consent

View File

@ -1,10 +1,11 @@
--- change_modules --- change_modules
-- Depends on consent
-- @module change_modules -- @module change_modules
local change_modules = { local change_modules = {
name = "Change modules", name = "Change modules (consent)",
slug = "change_modules", slug = "change_modules",
desc = "Add or remove modules from the org", desc = "Add or remove modules from the org with member consent",
hide = false; hide = false;
} }
@ -16,7 +17,6 @@ change_modules.data = {
} }
change_modules.config = { change_modules.config = {
approval_module = false
} }
--- Initiate change in modules. --- Initiate change in modules.
@ -68,7 +68,7 @@ function change_modules:initiate(result)
self.org:delete_process(self.id) self.org:delete_process(self.id)
return return
end end
-- proceed with approval -- proceed with consent
local query = "Accept module changes in org ".. local query = "Accept module changes in org "..
self.org.name.."?" self.org.name.."?"
self.data.summary = "" self.data.summary = ""
@ -80,10 +80,11 @@ function change_modules:initiate(result)
table.concat(self.data.remove_modules,", ") table.concat(self.data.remove_modules,", ")
end end
self:call_module( self:call_module(
self.config.approval_module, "consent",
self.initiator, self.initiator,
{ {
prompt = query..self.data.summary prompt = query..self.data.summary,
votes_required = #self.org.members
}, },
function() function()
self:implement_change() self:implement_change()

View File

@ -9,11 +9,11 @@ local change_policy = {
} }
change_policy.data = { change_policy.data = {
result = false result = nil
} }
change_policy.config = { change_policy.config = {
approval_module = false approval = "none"
} }
--- Change modules initiate --- Change modules initiate
@ -23,7 +23,8 @@ function change_policy:initiate(result)
-- prepare module options -- prepare module options
local available_modules = {} local available_modules = {}
for k,org_mod in pairs(modpol.modules) do for k,org_mod in pairs(modpol.modules) do
if self.org.policies[k] then if not org_mod.hide and
self.org.policies[k] then
available_modules[org_mod.slug] = modpol.util.copy_table(org_mod) available_modules[org_mod.slug] = modpol.util.copy_table(org_mod)
end end end end
local modules_list = {} local modules_list = {}
@ -68,20 +69,12 @@ function change_policy:initiate(result)
self.initiator, "Choose a policy to change:", self.initiator, "Choose a policy to change:",
policy_list, policy_list,
function(policy_choice) function(policy_choice)
local readable_value =
tostring(modpol.modules[mod_choice][policy_choice])
if readable_value == "nil" then
readable_value = "none"
end
modpol.interactions.text_query( modpol.interactions.text_query(
self.initiator, self.initiator,
"Current " .. policy_choice .. " value: " "Current " .. policy_choice .. " value: " ..
.. readable_value tostring(modpol.modules[mod_choice][policy_choice])
.. "\nChange value to (or leave blank):", .. "\nChange value to (be careful!):",
function(policy_input) function(policy_input)
if policy_input == "" then
policy_input = false
end
self:approve_change( self:approve_change(
mod_choice, mod_choice,
policy_choice, policy_choice,
@ -101,24 +94,17 @@ end
-- @param policy (string) policy slug -- @param policy (string) policy slug
-- @param input (string) input content -- @param input (string) input content
function change_policy:approve_change(module_slug, policy, input) function change_policy:approve_change(module_slug, policy, input)
self.org:call_module( -- NEED TO ADD APPROVAL CODE for consent, etc.
self.config.approval_module, modpol.interactions.message(
self.initiator, self.initiator,
{prompt = "Update " .. policy .. " policy on module " .. "Updating " .. policy .. " policy on module " ..
module_slug .. " with: " .. input .. " ?"}, module_slug .. " with: " .. input)
function() self.org.policies[module_slug][policy] = input
modpol.interactions.message( modpol.interactions.org_dashboard(
self.initiator, self.initiator, self.org.id)
"In ".. self.org.name .. " updating " .. policy .. if self.data.result then self.data.result() end
" policy on module " .. module_slug .. " with: " .. input) self.org:delete_process(self.id)
self.org.policies[module_slug][policy] = input return
modpol.interactions.org_dashboard(
self.initiator, self.org.id)
if self.data.result then self.data.result() end
self.org:delete_process(self.id)
end,
self.id
)
end end
modpol.modules.change_policy = change_policy modpol.modules.change_policy = change_policy

View File

@ -2,10 +2,10 @@
-- @module consent -- @module consent
local consent = { local consent = {
name = "Consent process", name = "Consent process",
slug = "consent", slug = "consent",
desc = "A utility module other modules use for consent decisions", desc = "A utility module other modules use for consent decisions",
hide = true hide = true
} }
consent.data = { consent.data = {
@ -13,8 +13,8 @@ consent.data = {
} }
consent.config = { consent.config = {
prompt = "Do you consent?", prompt = "Do you consent?",
votes_required = false votes_required = 1
} }
--- Initiate consent --- Initiate consent
@ -22,22 +22,13 @@ consent.config = {
-- @param result Callback if this module is embedded in other modules -- @param result Callback if this module is embedded in other modules
function consent:initiate(result) function consent:initiate(result)
self.data.result = result self.data.result = result
-- if org is empty or no votes required, consent given -- if org is empty, consent is given automatically
if self.org:get_member_count() == 0 if self.org:get_member_count() == 0 then
or self.config.votes_required == 0 then
modpol.interactions.message_org(
self.initiator,
self.org.name,
"Consent reached: " .. self.config.prompt)
if self.data.result then if self.data.result then
self.data.result() end self.data.result() end
self.org:delete_process(self.id) self.org:delete_process(self.id)
else else
-- otherwise, create poll -- otherwise, create poll
-- set default votes_required
if not self.config.votes_required then
self.config.votes_required = self.org:get_member_count()
end
for id, member in pairs(self.org.members) do for id, member in pairs(self.org.members) do
self.org:add_pending_action(self.id, member, "callback") self.org:add_pending_action(self.id, member, "callback")
end end
@ -63,10 +54,6 @@ function consent:callback(member)
"/"..self.config.votes_required..")" "/"..self.config.votes_required..")"
) )
if self.data.votes >= self.config.votes_required then if self.data.votes >= self.config.votes_required then
modpol.interactions.message_org(
self.initiator,
self.org.name,
"Consent reached: " .. self.config.prompt)
if self.data.result then if self.data.result then
self.data.result() end self.data.result() end
self.org:delete_process(self.id) self.org:delete_process(self.id)

View File

@ -3,9 +3,9 @@
-- @module create_token -- @module create_token
local create_token = { local create_token = {
name = "Create a token", name = "Create a token (consent)",
slug = "create_token", slug = "create_token",
desc = "Creates an org token", desc = "With org consent, creates an org token",
hide = false; hide = false;
} }
@ -13,8 +13,7 @@ create_token.data = {
} }
create_token.config = { create_token.config = {
token_name = "token", token_name = ""
approval_module = false
} }
--- Initiate function --- Initiate function
@ -30,7 +29,7 @@ function create_token:initiate(result)
"tokenomics", "tokenomics",
self.initiator, self.initiator,
{ {
approval_module = self.config.approval_module, consent = true,
token_slug = self.config.token_name token_slug = self.config.token_name
}, },
function(input2) function(input2)

View File

@ -1,30 +1,30 @@
--- Defer --- Defer consent
-- @module defer -- @module defer_consent
local defer = { local defer_consent = {
name = "Defer ", name = "Defer consent",
slug = "defer", slug = "defer_consent",
desc = "Defers a decision to another org", desc = "Defers consent on a decision to another org",
hide = true; hide = true;
} }
defer.data = { defer_consent.data = {
} }
--- Config for module --- Config for module
-- @field defer_org Name or ID of target org -- @field defer_org Name or ID of target org
-- @field approval_module module to use in target org -- @field votes_required Threshold passed on to `consent`
-- @field prompt String passed on to approval_module -- @field prompt String passed on to `consent`
defer.config = { defer_consent.config = {
approval_module = "consent",
defer_org = "Root", defer_org = "Root",
prompt = "" votes_required = 1,
prompt = "Do you consent?"
} }
--- Initiate function --- Initiate function
-- @param result Callback if this module is embedded in other modules -- @param result Callback if this module is embedded in other modules
-- @function defer:initiate -- @function defer_consent:initiate
function defer:initiate(result) function defer_consent:initiate(result)
local defer_org = modpol.orgs.get_org(self.config.defer_org) local defer_org = modpol.orgs.get_org(self.config.defer_org)
if not defer_org then if not defer_org then
modpol.interactions.message( modpol.interactions.message(
@ -32,21 +32,18 @@ function defer:initiate(result)
self.org:delete_process(self.id) self.org:delete_process(self.id)
else else
defer_org:call_module( defer_org:call_module(
self.config.approval_module, "consent", self.initiator,
self.initiator,
{ {
votes_required = self.config.votes_required,
prompt = self.config.prompt prompt = self.config.prompt
}, },
function() function()
if result then result() end if result then result() end
end) end)
modpol.interactions.message(
self.initiator, "Defer: action sent to " .. defer_org.name)
end end
modpol.interactions.org_dashboard( if result then result() end
self.initiator, self.org.id)
self.org:delete_process(self.id) self.org:delete_process(self.id)
end end
--- (Required) Add to module table --- (Required) Add to module table
modpol.modules.defer = defer modpol.modules.defer_consent = defer_consent

View File

@ -29,15 +29,11 @@ function display_policies:initiate(result)
v2 = self.org.policies[k][k2] v2 = self.org.policies[k][k2]
end end
local v2_string = "" local v2_string = ""
if not v2 then if type(v2) == "string" then
v2_string = "none"
elseif type(v2) == "string" then
v2_string = v2 v2_string = v2
elseif type(v2) == "table" elseif type(v2) == "table"
or type(v2) == "number" then or type(v2) == "number" then
v2_string = tostring(v2) v2_string = tostring(v2)
elseif type(v2) == "boolean" then
v2_string = tostring(v2)
else else
v2_string = "Could not render" v2_string = "Could not render"
end end
@ -57,10 +53,10 @@ function display_policies:initiate(result)
"Policies in org "..self.org.name, "Policies in org "..self.org.name,
output, output,
function() function()
self.org:delete_process(self.id)
modpol.interactions.org_dashboard( modpol.interactions.org_dashboard(
self.initiator, self.org.id) self.initiator, self.org.id)
if result then result() end if result then result() end
self.org:delete_process(self.id)
end end
) )
end end

View File

@ -19,7 +19,7 @@ display_processes.config = {
-- @param result Callback if this module is embedded in other modules -- @param result Callback if this module is embedded in other modules
function display_processes:initiate(result) function display_processes:initiate(result)
local display_table = {} local display_table = {}
for k,v in ipairs(self.org.processes) do for k,v in pairs(self.org.processes) do
if v ~= "deleted" then if v ~= "deleted" then
local input = v.id..": "..v.slug local input = v.id..": "..v.slug
table.insert(display_table, input) table.insert(display_table, input)

View File

@ -0,0 +1,3 @@
change_modules-dropdown.lua
join_org.lua
template.lua

View File

@ -1,55 +1,44 @@
--- Join org --- Adds a user to org
-- Adds initiator to an org
-- @module join_org -- @module join_org
local join_org = { join_org = {}
name = "Join this org",
slug = "join_org", join_org.setup = {
desc = "Allows initiator to join this org" name = "Join Org",
slug = "join_org",
desc = "If consent process is passed, initiator joins this org."
} }
join_org.data = { --- Adds the user to the org
result = nil -- @function join_org.initiate
}
join_org.config = {
approval_module = false
}
--- Initiate join org with consent
-- @function join_org:initiate
-- @param result Callback if this module is embedded in other modules -- @param result Callback if this module is embedded in other modules
function join_org:initiate(result) function join_org.initiate(result)
if self.org:has_member(self.initiator) then modpol.interactions.binary_poll_user(
modpol.interactions.message( initiator,
self.initiator, "Would you like to join " .. org.name,
"You are already a member of this org") function (resp)
if result then result() end if resp == "Yes" then
self.org:delete_process(self.id) self.org:add_member(self.initiator)
else end
self.data.result = result
self:call_module(
self.config.approval_module,
self.initiator,
{
prompt = "Allow " .. self.initiator .. " to join?"
},
function ()
self:complete()
end end
) )
end
end
--- Adds member to org, notifies org, and deletes process for i, member in ipairs(self.org.members) do
-- @function join_org:complete self.org:add_pending_action(
function join_org:complete() member,
self.org:add_member(self.initiator) function ()
modpol.interactions.message_org( modpol.interactions.binary_poll_user(
self.initiator,self.org.name, member,
self.initiator .. " joined org " .. self.org.name) "Let " .. initiator .. " join " .. org.name .. "?",
if self.data.result then self.data.result() end function (resp)
self.org:delete_process(self.id)
end
)
end
)
end
if result then result() end
end end
modpol.modules.join_org = join_org modpol.modules.join_org = join_org

View File

@ -0,0 +1,57 @@
--- Join org (consent).
-- A simple module that calls a consent process on an org to add a member.
-- Depends on the Consent module.
-- @module join_org_consent
local join_org_consent = {
name = "Join this org (consent)",
slug = "join_org_consent",
desc = "Adds member with consent of all members"
}
join_org_consent.data = {
result = nil
}
join_org_consent.config = {
}
--- Initiate join org with consent
-- @function join_org_consent:initiate
-- @param result Callback if this module is embedded in other modules
function join_org_consent:initiate(result)
if self.org:has_member(self.initiator) then
modpol.interactions.message(
self.initiator,
"You are already a member of this org")
if result then result() end
self.org:delete_process(self.id)
else
self.data.result = result
self:call_module(
"consent",
self.initiator,
{
prompt = "Allow " .. self.initiator .. " to join?",
votes_required = #self.org.members
},
function ()
self:complete()
end
)
end
end
--- Adds member to org, notifies org, and deletes process
-- @function join_org_consent:complete
function join_org_consent:complete()
self.org:add_member(self.initiator)
modpol.interactions.message_org(
self.initiator,self.org.name,
"Consent reached: " .. self.initiator ..
" joined org " .. self.org.name)
if self.data.result then self.data.result() end
self.org:delete_process(self.id)
end
modpol.modules.join_org_consent = join_org_consent

View File

@ -1,26 +1,26 @@
--- Remove child org --- Remove child (consent).
-- A simple module that calls a process on an org to remove its child. -- A simple module that calls a consent process on an org to remove its child.
-- @module remove_child_org -- Depends on the Consent module.
-- @module remove_child_consent
local remove_child_org = { local remove_child_consent = {
name = "Remove child org", name = "Remove child (consent)",
slug = "remove_child_org", slug = "remove_child_consent",
desc = "Removes a child org." desc = "Removes a child org if all members of this org consent."
} }
remove_child_org.data = { remove_child_consent.data = {
result = nil, result = nil,
child_to_remove = nil child_to_remove = nil
} }
remove_child_org.config = { remove_child_consent.config = {
approval_module = false
} }
--- Removes a child org with consent --- Removes a child org with consent
-- @function remove_child_org:initiate -- @function remove_child_consent:initiate
-- @param result Callback if this module is embedded in other modules -- @param result Callback if this module is embedded in other modules
function remove_child_org:initiate(result) function remove_child_consent:initiate(result)
local children = {} local children = {}
for i,v in ipairs(self.org.children) do for i,v in ipairs(self.org.children) do
local child = modpol.orgs.get_org(v) local child = modpol.orgs.get_org(v)
@ -43,10 +43,11 @@ function remove_child_org:initiate(result)
function(input) function(input)
self.data.child_to_remove = modpol.orgs.get_org(input) self.data.child_to_remove = modpol.orgs.get_org(input)
self:call_module( self:call_module(
self.config.approval_module, "consent",
self.initiator, self.initiator,
{ {
prompt = "Remove child org "..input.."?" prompt = "Remove child org "..input.."?",
votes_required = #self.org.members
}, },
function() function()
self:complete() self:complete()
@ -58,18 +59,19 @@ function remove_child_org:initiate(result)
end end
--- Complete the remove process --- Complete the remove process
-- @function remove_child_org:complete -- @function remove_child_consent:complete
function remove_child_org:complete() function remove_child_consent:complete()
modpol.interactions.message_org( modpol.interactions.message_org(
self.initiator, self.data.child_to_remove.id, self.initiator, self.data.child_to_remove.id,
"Removing this org: " .. self.data.child_to_remove.name) "Removing org " .. self.data.child_to_remove.name ..
" by parent org consent")
modpol.interactions.message_org( modpol.interactions.message_org(
self.initiator, self.org.id, self.initiator, self.org.id,
"Removing child org of " .. self.org.name .. ": " .. "Consent reached: removing org " ..
self.data.child_to_remove.name) self.data.child_to_remove.name)
self.data.child_to_remove:delete() self.data.child_to_remove:delete()
if self.data.result then self.data.result() end if self.data.result then self.data.result() end
self.org:delete_process(self.id) self.org:delete_process(self.id)
end end
modpol.modules.remove_child_org = remove_child_org modpol.modules.remove_child_consent = remove_child_consent

View File

@ -1,32 +1,29 @@
--- Removes member from org --- Calls consent to remove member from org
-- @module remove_member -- @module remove_member_consent
local remove_member = { local remove_member_consent = {
name = "Remove a member", name = "Remove a member (consent)",
slug = "remove_member", slug = "remove_member_consent",
desc = "Removes org member" desc = "Removes org member with consent of other members"
} }
remove_member.data = { remove_member_consent.data = {
member_to_remove = "", member_to_remove = "",
result = nil result = nil
} }
remove_member.config = { remove_member_consent.config = {
approval_module = false
} }
--- Removes given member from org --- Removes given member from org
-- @function remove_member:initiate -- @function remove_member_consent:initiate
-- @param result Callback if this module is embedded in other modules -- @param result Callback if this module is embedded in other modules
function remove_member:initiate(result) function remove_member_consent:initiate(result)
-- Abort if in root org -- Abort if in root org
if self.org == modpol.instance then if self.org == modpol.instance then
modpol.interactions.message( modpol.interactions.message(
self.initiator, self.initiator,
"Members cannot be removed from the root org") "Members cannot be removed from the root org")
modpol.interactions.org_dashboard(
self.initiator, self.org.name)
if result then result() end if result then result() end
self.org:delete_process(self.id) self.org:delete_process(self.id)
else -- proceed if not root else -- proceed if not root
@ -39,11 +36,12 @@ function remove_member:initiate(result)
function(input) function(input)
self.data.member_to_remove = input self.data.member_to_remove = input
self:call_module( self:call_module(
self.config.approval_module, "consent",
self.initiator, self.initiator,
{ {
prompt = "Remove "..input.. prompt = "Remove "..input..
" from org "..self.org.name.."?" " from org "..self.org.name.."?",
votes_required = #self.org.members - 1
}, },
function() function()
self:complete() self:complete()
@ -55,11 +53,11 @@ function remove_member:initiate(result)
end end
--- Complete after consent --- Complete after consent
-- @function remove_member:complete -- @function remove_member_consent:complete
function remove_member:complete() function remove_member_consent:complete()
modpol.interactions.message_org( modpol.interactions.message_org(
self.initiator, self.org.id, self.initiator, self.org.id,
"Removing ".. "Consent reached: removing "..
self.data.member_to_remove.. self.data.member_to_remove..
" from org "..self.org.name) " from org "..self.org.name)
self.org:remove_member(self.data.member_to_remove) self.org:remove_member(self.data.member_to_remove)
@ -67,4 +65,4 @@ function remove_member:complete()
if self.data.result then self.data.result() end if self.data.result then self.data.result() end
end end
modpol.modules.remove_member = remove_member modpol.modules.remove_member_consent = remove_member_consent

View File

@ -1,57 +1,32 @@
--- Remove org --- A simple module that removes an org.
-- Removes the current org
-- @module remove_org -- @module remove_org
local remove_org = { remove_org = {
name = "Remove this org", name = "Remove this org",
slug = "remove_org", slug = "remove_org",
desc = "Removes this org." desc = "Eliminates the org and all child orgs."
} }
remove_org.data = { remove_org.config = {}
result = nil remove_org.data = {}
}
remove_org.config = { --- Removes org
approval_module = false
}
--- Remove org if all members consent
-- @function remove_org:initiate -- @function remove_org:initiate
-- @param result Callback if this module is embedded in other modules -- @param result Callback if this module is embedded in other modules
function remove_org:initiate(result) function remove_org:initiate(result)
if self.org == modpol.instance then if self.org == modpol.instance then
modpol.interactions.message( modpol.interactions.message(
self.initiator, self.initiator,
"Cannot remove root org") "Cannot remove the root org")
if result then result() end
self.org:delete_process(self.id)
else else
self.data.result = result modpol.interactions.message_org(
self:call_module( self.initiator,self.org.id,
self.config.approval_module, "Removing org: "..self.org.name)
self.initiator, self.org:delete()
{ modpol.interactions.dashboard(self.initiator)
prompt = "Remove org " .. self.org.name .. "?" -- call result function
},
function()
self:complete()
end
)
modpol.interactions.org_dashboard(
self.initiator, modpol.orgs.get_org(self.org.parent).name)
end end
end if result then result() end
--- Complete after approval
-- @function remove_org:complete
function remove_org:complete()
modpol.interactions.message_org(
self.initiator, self.org.id,
"Removing org " .. self.org.name)
if self.data.result then self.data.result() end
self.org:delete_process(self.id)
self.org:delete()
end end
modpol.modules.remove_org = remove_org modpol.modules.remove_org = remove_org

View File

@ -0,0 +1,58 @@
--- Remove org (consent)
-- A simple module that calls a consent process on an org to remove it.
-- Depends on the Consent module.
-- @module remove_org_consent
local remove_org_consent = {
name = "Remove this org (consent)",
slug = "remove_org_consent",
desc = "Removes an org if all members consent."
}
remove_org_consent.data = {
result = nil
}
remove_org_consent.config = {
}
--- Remove org if all members consent
-- @function remove_org_consent:initiate
-- @param result Callback if this module is embedded in other modules
function remove_org_consent:initiate(result)
if self.org == modpol.instance then
modpol.interactions.message(
self.initiator,
"Cannot remove root org")
if result then result() end
self.org:delete_process(self.id)
else
self.data.result = result
self:call_module(
"consent",
self.initiator,
{
prompt = "Remove org " .. self.org.name .. "?",
votes_required = #self.org.members
},
function()
self:complete()
end
)
modpol.interactions.org_dashboard(
self.initiator, self.org.name)
end
end
--- Complete after consent
-- @function remove_org_consent:complete
function remove_org_consent:complete()
modpol.interactions.message_org(
self.initiator, self.org.id,
"Consent reached: removing org " .. self.org.name)
if self.data.result then self.data.result() end
self.org:delete_process(self.id)
self.org:delete()
end
modpol.modules.remove_org_consent = remove_org_consent

View File

@ -4,7 +4,7 @@
local remove_process = { local remove_process = {
name = "Remove process", name = "Remove process",
slug = "remove_process", slug = "remove_process",
desc = "User can remove own processes, approval required for those of others", desc = "User can remove own processes, consent required for those of others",
hide = false; hide = false;
} }
@ -12,7 +12,6 @@ remove_process.data = {
} }
remove_process.config = { remove_process.config = {
approval_module = "consent"
} }
--- Initiate function --- Initiate function
@ -72,12 +71,11 @@ function remove_process:initiate(result)
self.org:delete_process(self.id) self.org:delete_process(self.id)
else else
self:call_module( self:call_module(
self.config.approval_module, "consent",
self.initiator, self.initiator,
{ {
prompt = prompt = "Approve removal of process "..process_choice.."?",
"Approve removal of process " votes_required = #self.org.members
..process_choice.."?"
}, },
function(input) function(input)
modpol.interactions.message_org( modpol.interactions.message_org(

View File

@ -1,26 +1,26 @@
--- Rename org --- Rename org (consent)
-- Calls a process on an org to rename it. -- A simple module that calls a consent process on an org to rename it.
-- @module rename_org -- Depends on the Consent module.
-- @module rename_org_consent
local rename_org = { local rename_org_consent = {
name = "Rename this org", name = "Rename this org (consent)",
slug = "rename_org", slug = "rename_org_consent",
desc = "Renames an org." desc = "Renames an org if all members consent."
} }
rename_org.data = { rename_org_consent.data = {
result = nil, result = nil,
new_name = nil new_name = nil
} }
rename_org.config = { rename_org_consent.config = {
approval_module = false
} }
--- Renames the org after consent is reached --- Renames the org after consent is reached
-- @function rename_org:initiate -- @function rename_org_consent:initiate
-- @param result Callback if this module is embedded in other modules -- @param result Callback if this module is embedded in other modules
function rename_org:initiate(result) function rename_org_consent:initiate(result)
modpol.interactions.text_query( modpol.interactions.text_query(
self.initiator,"New org name: ", self.initiator,"New org name: ",
function(input) function(input)
@ -50,11 +50,12 @@ function rename_org:initiate(result)
self.org.name .. " to " .. input) self.org.name .. " to " .. input)
-- initiate consent process -- initiate consent process
self:call_module( self:call_module(
self.config.approval_module, "consent",
self.initiator, self.initiator,
{ {
prompt = "Change name of org " .. prompt = "Change name of org " ..
self.org.name .. " to " .. input .. "?" self.org.name .. " to " .. input .. "?",
votes_required = #self.org.members
}, },
function() function()
self:complete() self:complete()
@ -66,9 +67,9 @@ function rename_org:initiate(result)
) )
end end
--- Changes the name of the org --- Changes the name of the org after consent is reached
-- @funciton rename_org -- @funciton rename_org_consent
function rename_org:complete() function rename_org_consent:complete()
modpol.interactions.message_org( modpol.interactions.message_org(
self.initiator, self.initiator,
self.org.name, self.org.name,
@ -79,4 +80,4 @@ function rename_org:complete()
self.org:delete_process(self.id) self.org:delete_process(self.id)
end end
modpol.modules.rename_org = rename_org modpol.modules.rename_org_consent = rename_org_consent

View File

@ -16,7 +16,7 @@ send_token.data = {
} }
send_token.config = { send_token.config = {
token_name = nil -- hidden configuration token_name = ""
} }
--- initiate function --- initiate function
@ -29,7 +29,7 @@ function send_token:initiate(result)
table.insert(token_list, k) table.insert(token_list, k)
end end
end end
if #token_list == 0 then if token_list == {} then
modpol.interactions.message( modpol.interactions.message(
self.initiator, self.initiator,
"No tokens in org") "No tokens in org")

View File

@ -26,12 +26,9 @@ module_template.data = {
-- When calling a module from within another module, -- When calling a module from within another module,
-- variables not defined in config will be ignored. -- variables not defined in config will be ignored.
-- Default values set in config can be overridden -- Default values set in config can be overridden
-- @field approval_module names a module that must pass before approval; defaults to false
-- @field field_1 ex: votes_required, default = 5 -- @field field_1 ex: votes_required, default = 5
-- @field field_2 ex: voting_type, default = "majority" -- @field field_2 ex: voting_type, default = "majority"
module_template.config = { module_template.config = {
approval_module = false -- visible but empty
hidden_config = nil -- not visible to users unless used
field_1 = 5 field_1 = 5
field_2 = "majority" field_2 = "majority"
} }

View File

@ -1,4 +1,5 @@
--- Tokenomics. --- Tokenomics.
-- Depends on consent
-- @module tokenomics -- @module tokenomics
local tokenomics = { local tokenomics = {
@ -13,13 +14,14 @@ tokenomics.data = {
} }
--- Config for module --- Config for module
-- @field consent Require consent to create?
-- @field token_variables the data that goes into the token -- @field token_variables the data that goes into the token
-- @field token_slug A no-spaces slug for the token -- @field token_slug A no-spaces slug for the token
-- @field initial_treasury Quantity in org treasury -- @field initial_treasury Quantity in org treasury
-- @field negative_spend Boolean: can users spend negative tokens? (for mutual credit) -- @field negative_spend Boolean: can users spend negative tokens? (for mutual credit)
-- @field balances Table of user balances -- @field balances Table of user balances
tokenomics.config = { tokenomics.config = {
approval_module = false, consent = false,
token_slug = "token", token_slug = "token",
token_variables = { token_variables = {
treasury = 0, treasury = 0,
@ -41,25 +43,29 @@ function tokenomics:initiate(result)
self.initiator, "Token slug taken, aborting") self.initiator, "Token slug taken, aborting")
self.org:delete_process(self.id) self.org:delete_process(self.id)
else else
self:call_module( if self.config.consent then
self.config.approval_module, self:call_module(
self.initiator, "consent",
{ self.initiator,
prompt = "Create token " .. {
self.config.token_slug .. "?" prompt = "Create token "..
}, self.config.token_slug.."?",
function() votes_required = #self.org.members
modpol.interactions.message_org( },
self.initiator, self.org.id, function()
"Creating token " .. modpol.interactions.message_org(
self.config.token_slug) self.initiator, self.org.id,
self:create_token() "Consent reached: creating token "..
end self.config.token_slug)
) self:create_token()
end
)
else
self:create_token()
end
end end
end end
--- Create token --- Create token
-- @function tokenomics:create_token -- @function tokenomics:create_token
function tokenomics:create_token() function tokenomics:create_token()

View File

@ -25,24 +25,19 @@ end
--- Return org when given its id or name --- Return org when given its id or name
-- @function modpol.orgs.get_org -- @function modpol.orgs.get_org
-- @param arg string for name of org or id of org, or nil if no org -- @param arg string for name of org or id of org
-- @return org specified by id or name -- @return org specified by id or name
function modpol.orgs.get_org(arg) function modpol.orgs.get_org(arg)
local output = nil if type(arg) == 'string' then
if type(arg) == 'string' then for id, org in ipairs(modpol.orgs.array) do
for id, org in ipairs(modpol.orgs.array) do
if org ~= "deleted" then
if org.name == arg then if org.name == arg then
output = org return org
end end
end end
end elseif type(arg) == 'number' then
elseif type(arg) == 'number' then return modpol.orgs.array[arg]
if modpol.orgs.array[arg] ~= "deleted" then end
output = modpol.orgs.array[arg] return nil
end
end
return output
end end
--- Return a table list of all org names --- Return a table list of all org names
@ -85,7 +80,7 @@ function modpol.orgs.reset()
modpol.util.copy_table(modpol.instance.members) modpol.util.copy_table(modpol.instance.members)
for id, org in ipairs(modpol.orgs.array) do for id, org in ipairs(modpol.orgs.array) do
if id > 1 then if id > 1 then
modpol.orgs.array[id] = "deleted" modpol.orgs.array[id] = "removed"
end end
end end
@ -219,18 +214,7 @@ function modpol.orgs:delete()
modpol.ocutil.log('Error in ' .. self.name .. ':delete -> cannot delete instance') modpol.ocutil.log('Error in ' .. self.name .. ':delete -> cannot delete instance')
return false return false
end end
-- remove from parent's list
local parent = modpol.orgs.get_org(self.id)
if parent then
for k,v in ipairs(parent.children) do
if v == self.id then
v = "deleted"
end
end
end
-- remove children
if #self.children > 0 then if #self.children > 0 then
for i, child_id in pairs(self.children) do for i, child_id in pairs(self.children) do
local child = modpol.orgs.get_org(child_id) local child = modpol.orgs.get_org(child_id)
@ -239,8 +223,7 @@ function modpol.orgs:delete()
end end
end end
modpol.orgs.array[self.id] = 'deleted' modpol.orgs.array[self.id] = 'removed'
modpol.orgs.count = modpol.orgs.count - 1
modpol.ocutil.log('Deleted org ' .. self.name .. ': ' .. self.id) modpol.ocutil.log('Deleted org ' .. self.name .. ': ' .. self.id)
self:record('Deleted ' .. self.name .. ' and all child orgs', 'del_org') self:record('Deleted ' .. self.name .. ' and all child orgs', 'del_org')

View File

@ -3,28 +3,19 @@
--- Call modules --- Call modules
-- @function modpol.orgs.call_module -- @function modpol.orgs.call_module
-- @param module_slug Same as module name (or false to run result) -- @param module_slug Same as module name
-- @param intiator Initiator for module -- @param intiator Initiator for module
-- @param config Config for module -- @param config Config for module
-- @param result -- @param result
function modpol.orgs:call_module(module_slug, initiator, config, result, parent_id) function modpol.orgs:call_module(module_slug, initiator, config, result, parent_id)
if not modpol.modules[module_slug] then
-- first, if no slug, just run result modpol.ocutil.log('Error in ' .. self.name .. ':call_module -> module "' .. module_slug .. '" not found')
-- may not be necessary if we use false as default approval_module return
if not module_slug then end
if result() then
result()
end
return
-- if module doesn't exist, abort
elseif not modpol.modules[module_slug] then
modpol.ocutil.log('Error in ' .. self.name .. ':call_module -> module "' .. tostring(module_slug) .. '" not found')
return
end
local index = #self.processes + 1 local index = #self.processes + 1
local module = modpol.modules[module_slug] local module = modpol.modules[module_slug]
-- first applies any relevant org policies -- first applies any relevant org policies
-- then overrides with the config values given on input -- then overrides with the config values given on input
@ -73,8 +64,6 @@ function modpol.orgs:call_module(module_slug, initiator, config, result, parent_
return index return index
end end
--- Get the root process of the given id --- Get the root process of the given id
-- @function modpol.orgs.get_root_process -- @function modpol.orgs.get_root_process
-- @param id -- @param id
@ -87,7 +76,7 @@ function modpol.orgs:get_root_process(id)
return process return process
end end
--- Delete the process given id, return to dashboard --- Delete the process given id
-- @function modpol.orgs.delete_process -- @function modpol.orgs.delete_process
-- @param id -- @param id
function modpol.orgs:delete_process(id) function modpol.orgs:delete_process(id)
@ -108,7 +97,6 @@ function modpol.orgs:delete_process(id)
end end
end end
--- Delete process tree by id --- Delete process tree by id
-- @function modpol.orgs:delete_process_tree -- @function modpol.orgs:delete_process_tree
-- @param id Id of process tree -- @param id Id of process tree
@ -134,7 +122,7 @@ end
-- @param user -- @param user
function modpol.orgs:remove_pending_action(process_id, user) function modpol.orgs:remove_pending_action(process_id, user)
if self.pending[user] then if self.pending[user] then
self.pending[user][process_id] = "deleted" self.pending[user][process_id] = nil
end end
end end
@ -143,7 +131,7 @@ end
-- @param process_id -- @param process_id
function modpol.orgs:wipe_pending_actions(process_id) function modpol.orgs:wipe_pending_actions(process_id)
for user in pairs(self.pending) do for user in pairs(self.pending) do
self.pending[user][process_id] = "deleted" self.pending[user][process_id] = nil
end end
end end
@ -165,7 +153,7 @@ function modpol.orgs:has_pending_actions(user)
end end
end end
--- Create user interaction with given process --- Interact a user with given process
-- @function modpol.orgs:interact -- @function modpol.orgs:interact
-- @param process_id -- @param process_id
-- @param user -- @param user

View File

@ -11,6 +11,8 @@ modpol.file_ledger = modpol.datadir .. "/ledger.dat"
modpol.file_orgs = modpol.datadir .. "/orgs.dat" modpol.file_orgs = modpol.datadir .. "/orgs.dat"
modpol.file_old_ledgers = modpol.datadir .. "/old_ledgers.dat" modpol.file_old_ledgers = modpol.datadir .. "/old_ledgers.dat"
os.execute ("mkdir -p " .. modpol.datadir)
modpol.ocutil.setlogdir (modpol.datadir) modpol.ocutil.setlogdir (modpol.datadir)
modpol.ocutil.setlogname ("modpol.log") modpol.ocutil.setlogname ("modpol.log")
@ -99,7 +101,7 @@ local load_orgs = function()
-- setmetatable(org, modpol.orgs) -- setmetatable(org, modpol.orgs)
-- end -- end
local nn = modpol.orgs.count local nn = modpol.ocutil.table_length (modpol.orgs.array)
local str = "entries" local str = "entries"
if nn == 1 then str = "entry" end if nn == 1 then str = "entry" end
modpol.ocutil.log (nn .. " orgs loaded from disk") modpol.ocutil.log (nn .. " orgs loaded from disk")

View File

@ -0,0 +1,23 @@
modpol.load_modules = function(path)
dofile (path .. "/add_child_org_consent.lua")
dofile (path .. "/add_child_org.lua")
dofile (path .. "/change_modules.lua")
dofile (path .. "/change_policy.lua")
dofile (path .. "/consent.lua")
dofile (path .. "/create_token.lua")
dofile (path .. "/defer_consent.lua")
dofile (path .. "/display_policies.lua")
dofile (path .. "/display_processes.lua")
dofile (path .. "/join_org_consent.lua")
dofile (path .. "/leave_org.lua")
dofile (path .. "/message_org.lua")
dofile (path .. "/randomizer.lua")
dofile (path .. "/remove_child_consent.lua")
dofile (path .. "/remove_member_consent.lua")
dofile (path .. "/remove_org_consent.lua")
dofile (path .. "/remove_org.lua")
dofile (path .. "/remove_process.lua")
dofile (path .. "/rename_org_consent.lua")
dofile (path .. "/send_token.lua")
dofile (path .. "/tokenomics.lua")
end

View File

@ -0,0 +1,59 @@
local lfs
-- checks if lua file system is installed
local using_lfs = pcall(function() lfs = require "lfs" end)
-- switches to legacy module loading if lfs is not available
if using_lfs then
-- loads file names to ignore into a table
function fetch_ignores(module_path)
local ignore_list = {}
-- checks if ignore.txt exists
local f_test = io.open(module_path .. "/ignore.txt", "r")
if not f_test then return {} end
-- puts each line of ignore.txt into the table
local f = io.lines(module_path .. "/ignore.txt")
for line in f do
table.insert(ignore_list, line)
end
return ignore_list
end
-- checks if a string is in a list
function check_list(ignore_list, name)
for i, v in ipairs(ignore_list) do
if v == name then
return true
end
end
return false
end
modpol.load_modules = function(module_path)
local loaded = 0
local ignored = 0
local ignores = fetch_ignores(module_path)
for file in lfs.dir(module_path) do
if file == "." or file == ".." then
-- ignoring current and parent directory
else
-- only looks for .lua files
if string.sub(file, -4, -1) == ".lua" then
-- doesn't load files in the ignore.txt
if check_list(ignores, file) then
ignored = ignored + 1
else
dofile(module_path .. "/" .. file)
loaded = loaded + 1
end
end
end
end
print(loaded .. " modules loaded (" .. ignored .. " ignored)")
end
else
dofile (modpol.topdir .. "/storage/store-modules-legacy.lua")
end

View File

@ -0,0 +1,109 @@
-- ===================================================================
-- /unix-storage.lua
-- Persistent storage in /data using Serpent Serializer
-- Unix systems only
modpol.datadir = modpol.topdir .. "/data"
modpol.file_ledger = modpol.datadir .. "/ledger.dat"
modpol.file_orgs = modpol.datadir .. "/orgs.dat"
os.execute ("mkdir -p " .. modpol.datadir)
modpol.ocutil.setlogdir (modpol.datadir)
modpol.ocutil.setlogname ("modpol.log")
modpol.serpent = {}
dofile (modpol.topdir .. "/util/serpent/serpent.lua")
-- ===================================================================
local write_file = function(path, data)
local file, err
file, err = io.open(path, "w")
if err then print(err) os.exit(1) end
file:write(data)
file:close()
end
local read_file = function(path)
local file, err
file, err = io.open(path, "r")
if err then return nil end
local data = file:read()
file:close()
return data
end
-- ===================================================================
local store_ledger = function(verbose)
local serialized_ledger = modpol.serpent.dump(modpol.ledger)
write_file(modpol.file_ledger, serialized_ledger)
local count = 0
for _ in pairs(modpol.ledger) do count = count + 1 end
if verbose then modpol.ocutil.log(count .. " ledger entries stored to disk")
end
end
local store_orgs = function(verbose)
local serialized_orgs = modpol.serpent.dump(modpol.orgs)
write_file(modpol.file_orgs, serialized_orgs)
local count = 0
for _ in pairs(modpol.orgs.array) do count = count + 1 end
if verbose then modpol.ocutil.log(count .. " orgs stored to disk")
end
end
modpol.store_data = function(verbose)
store_ledger(verbose)
store_orgs(verbose)
end
-- ===================================================================
local load_ledger = function()
local obj = read_file(modpol.file_ledger)
if obj ~= nil then
local func, err = load(obj)
if err then
modpol.ocutil.log("Error loading ledger data")
os.exit(1)
end
modpol.ledger = func()
local count = 0
for _ in pairs(modpol.ledger) do count = count + 1 end
modpol.ocutil.log(count .. " ledger entries loaded from disk")
else
modpol.ocutil.log("No stored ledger data found")
end
end
local load_orgs = function()
local obj = read_file(modpol.file_orgs)
if obj ~= nil then
local func, err = load(obj)
if err then
modpol.ocutil.log("Error loading org data")
os.exit(1)
end
modpol.orgs = func()
local count = 0
for _ in pairs(modpol.orgs.array) do count = count + 1 end
modpol.ocutil.log(count .. " orgs loaded from disk")
else
modpol.ocutil.log("No stored orgs found")
end
end
modpol.load_storage = function()
load_ledger()
load_orgs()
end

View File

@ -32,13 +32,3 @@ function modpol.util.num_pairs(t)
end end
return i return i
end end
function modpol.util.lazy_table_length(tbl, lazy_val)
local count = 0
for k, v in ipairs(tbl) do
if v ~= lazy_val then
count = count + 1
end
end
return count
end

View File

@ -1 +0,0 @@
(placeholder)

View File

@ -137,15 +137,11 @@ function modpol.interactions.org_dashboard(user, org_string)
-- prepare children menu -- prepare children menu
local children = {} local children = {}
if org.children then for k,v in ipairs(org.children) do
for k,v in ipairs(org.children) do local this_child = modpol.orgs.get_org(v)
local this_child = modpol.orgs.get_org(v) table.insert(children, this_child.name)
if this_child then
table.insert(children, this_child.name)
end
end
table.sort(children)
end end
table.sort(children)
-- prepare modules menu -- prepare modules menu
local modules = {} local modules = {}