Compare commits

...

29 Commits

Author SHA1 Message Date
Nathan Schneider
13eb58a3ee Removed child org from parent children list 2022-08-26 15:59:38 -07:00
Nathan Schneider
ec0e4aa9c1 Small fix on policy_change variables 2022-08-23 17:11:00 -06:00
Nathan Schneider
b09a7a24b4 Improvement on error catching in process 2022-08-23 17:04:04 -06:00
Nathan Schneider
9d759530e6 Fixes on process logic 2022-08-23 16:55:57 -06:00
Nathan Schneider
838a017f14 Various bugfixes on defer and processes 2022-08-17 17:24:53 -06:00
Nathan Schneider
df20cc835b Small bugfixes 2022-08-17 15:39:11 -06:00
Nathan Schneider
0c13e6b084 Revert on CLI interactions pending process list 2022-08-17 15:09:12 -06:00
ntnsndr
d24262d494 small documentation fixes 2022-08-17 19:57:52 +00:00
23cf6abacd Merge branch 'bug-fixes' into 'master'
fixed process counter and a other small bugs

See merge request medlabboulder/modpol!37
2022-08-16 15:33:09 +00:00
Luke Miller
daa6d95bd6 merged changes, using standardized lazy table length function instead of for loop in interactions module 2022-08-16 11:12:07 -04:00
Luke Miller
52334f409e fixed process count, added lazy table length function, removed orgs now decrement org counter, orgs loaded message should now be accurate 2022-08-16 11:10:40 -04:00
56adaba7d6 Simplified README, reference to modpol.net 2022-08-16 04:49:40 +00:00
06e7bb9d2d Replaced image on README 2022-08-16 02:40:20 +00:00
Nathan Schneider
46dc48ffcc Fixed processes display in core except modules/display_processes.lua 2022-08-13 21:59:09 -06:00
Nathan Schneider
45347e2ac7 Merge branch 'master' of https://gitlab.com/medlabboulder/modpol 2022-08-13 17:02:58 -06:00
Nathan Schneider
1ff94b65fb Fixes on defer and remove_process 2022-08-13 17:02:30 -06:00
Nathan Schneider
4337e5511b Made approval on remove_process configurable 2022-08-13 16:34:03 -06:00
a26d2714d9 Merge branch 'remove-mkdir' into 'master'
Factored out Unix dependent os call

See merge request medlabboulder/modpol!36
2022-08-12 03:38:02 +00:00
Luke Miller
8d3428653c Merge branch 'master' of https://gitlab.com/medlabboulder/modpol 2022-08-11 14:57:42 -04:00
Luke Miller
6977614a7d Merge branch 'master' of https://gitlab.com/medlabboulder/modpol 2022-08-11 14:50:39 -04:00
Luke Miller
919194c995 added a placeholder file to keep data directory in git, removed now unnecessary unix mkdir call 2022-08-11 14:50:10 -04:00
Nathan Schneider
0532b084b7 Small correction on README 2022-08-11 10:49:03 -06:00
9b27ac145c Merge branch 'generic_approval' into 'master'
Major improvements on policy configuration

See merge request medlabboulder/modpol!35
2022-08-09 23:04:54 +00:00
Nathan Schneider
22a2048d5a Major improvements on policy configuration
- Bugfixes on change_policy
- Replaced _consent modules with configurable modules
2022-08-09 17:00:24 -06:00
Nathan Schneider
78ea89559f Simplier way of doing generic calls with call_module 2022-08-05 16:41:13 -06:00
Nathan Schneider
0ec287fa57 Reverting 2022-08-05 16:34:24 -06:00
Nathan Schneider
1f33232394 First shot at a generic approve() function for modules, testing on change_policy 2022-08-05 16:10:56 -06:00
Nathan Schneider
99c75861b0 small bugfix on display_processes 2022-05-25 12:30:59 -06:00
Nathan Schneider
26df04445d Small fix on CLI interactions on pending actions. 2022-05-25 12:19:34 -06:00
31 changed files with 392 additions and 486 deletions

3
.gitignore vendored
View File

@ -1 +1,2 @@
*/data
*/data/*
!*/data/placeholder

View File

@ -1,26 +1,12 @@
# 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.
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.
## How to use it
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!).
**Learn more at [modpol.net](https://modpol.net).**
## Installation in Minetest
@ -28,7 +14,7 @@ To use this in Minetest, simply install it in your `mods/` or `worldmods/` folde
In the game, open the Modpol dashboard with the command `/mp`.
For testing purposes, players with the `privs` privilege (generally admins) can use the `/mp` command, which resets all the orgs and opens a dashboard.\
For testing purposes, players with the `privs` privilege (generally admins) can use the `/mptest` command, which resets all the orgs and opens a dashboard.\
## Standalone Version on the Command Line
@ -55,23 +41,6 @@ In the interpreter, for a list of global functions and tables, use `modpol.menu(
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.
## 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
Various guides are available at the [GitLab wiki](https://gitlab.com/medlabboulder/modpol/-/wikis/home).
@ -101,7 +70,7 @@ We are grateful for initial support for this project from a residency with [The
## Contributing
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).
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).
Learn more about the project and how to develop your own modules in [the wiki](https://gitlab.com/medlabboulder/modpol/-/wikis/home).

BIN
lib/empire-modpol.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 588 KiB

View File

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

View File

View File

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

View File

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

View File

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

View File

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

View File

@ -2,10 +2,10 @@
-- @module consent
local consent = {
name = "Consent process",
slug = "consent",
desc = "A utility module other modules use for consent decisions",
hide = true
name = "Consent process",
slug = "consent",
desc = "A utility module other modules use for consent decisions",
hide = true
}
consent.data = {
@ -13,8 +13,8 @@ consent.data = {
}
consent.config = {
prompt = "Do you consent?",
votes_required = 1
prompt = "Do you consent?",
votes_required = false
}
--- Initiate consent
@ -22,13 +22,22 @@ consent.config = {
-- @param result Callback if this module is embedded in other modules
function consent:initiate(result)
self.data.result = result
-- if org is empty, consent is given automatically
if self.org:get_member_count() == 0 then
-- if org is empty or no votes required, consent given
if self.org:get_member_count() == 0
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
self.data.result() end
self.org:delete_process(self.id)
else
-- 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
self.org:add_pending_action(self.id, member, "callback")
end
@ -54,6 +63,10 @@ function consent:callback(member)
"/"..self.config.votes_required..")"
)
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
self.data.result() end
self.org:delete_process(self.id)

View File

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

View File

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

View File

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

View File

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

View File

@ -1,44 +1,55 @@
--- Adds a user to org
--- Join org
-- Adds initiator to an org
-- @module join_org
join_org = {}
join_org.setup = {
name = "Join Org",
slug = "join_org",
desc = "If consent process is passed, initiator joins this org."
local join_org = {
name = "Join this org",
slug = "join_org",
desc = "Allows initiator to join this org"
}
--- Adds the user to the org
-- @function join_org.initiate
join_org.data = {
result = nil
}
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
function join_org.initiate(result)
modpol.interactions.binary_poll_user(
initiator,
"Would you like to join " .. org.name,
function (resp)
if resp == "Yes" then
self.org:add_member(self.initiator)
end
function join_org: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(
self.config.approval_module,
self.initiator,
{
prompt = "Allow " .. self.initiator .. " to join?"
},
function ()
self:complete()
end
)
end
end
for i, member in ipairs(self.org.members) do
self.org:add_pending_action(
member,
function ()
modpol.interactions.binary_poll_user(
member,
"Let " .. initiator .. " join " .. org.name .. "?",
function (resp)
end
)
end
)
end
if result then result() end
--- Adds member to org, notifies org, and deletes process
-- @function join_org:complete
function join_org:complete()
self.org:add_member(self.initiator)
modpol.interactions.message_org(
self.initiator,self.org.name,
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 = join_org

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -26,9 +26,12 @@ module_template.data = {
-- When calling a module from within another module,
-- variables not defined in config will be ignored.
-- 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_2 ex: voting_type, default = "majority"
module_template.config = {
approval_module = false -- visible but empty
hidden_config = nil -- not visible to users unless used
field_1 = 5
field_2 = "majority"
}

View File

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

View File

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

View File

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

View File

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

View File

@ -32,3 +32,13 @@ function modpol.util.num_pairs(t)
end
return i
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

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