Resolved merge conflicts with master
This commit is contained in:
@ -5,6 +5,7 @@ local localdir = modpol.topdir
|
||||
--orgs
|
||||
dofile (localdir .. "/orgs/base.lua")
|
||||
dofile (localdir .. "/orgs/process.lua")
|
||||
dofile (localdir .. "/orgs/users.lua")
|
||||
|
||||
--interactions
|
||||
dofile (localdir .. "/interactions/interactions.lua")
|
||||
@ -14,11 +15,18 @@ dofile (localdir .. "/interactions/interactions.lua")
|
||||
dofile (localdir .. "/modules/add_child_org_consent.lua")
|
||||
dofile (localdir .. "/modules/change_modules.lua")
|
||||
dofile (localdir .. "/modules/consent.lua")
|
||||
dofile (localdir .. "/modules/create_token.lua")
|
||||
dofile (localdir .. "/modules/defer_consent.lua")
|
||||
dofile (localdir .. "/modules/display_processes.lua")
|
||||
dofile (localdir .. "/modules/join_org_consent.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_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")
|
||||
|
@ -38,8 +38,10 @@ function modpol.interactions.dashboard(user)
|
||||
local user_pending_count = 0
|
||||
for k,v in ipairs(modpol.orgs.array) do
|
||||
if v.pending and v.pending[user] then
|
||||
table.insert(user_pending, v.name)
|
||||
user_pending_count = user_pending_count + 1
|
||||
if modpol.util.num_pairs(v.pending[user]) ~= 0 then
|
||||
table.insert(user_pending, v.name)
|
||||
user_pending_count = user_pending_count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -48,18 +50,53 @@ function modpol.interactions.dashboard(user)
|
||||
print('All users: ' .. table.concat(all_users, ', '))
|
||||
print()
|
||||
|
||||
print('Access which org id?')
|
||||
print("Commands: (O)rg, (U)ser, (R)eset, (Q)uit")
|
||||
|
||||
local sel = io.read()
|
||||
print()
|
||||
|
||||
if modpol.orgs.array[tonumber(sel)] then
|
||||
local sel_org = modpol.orgs.array[tonumber(sel)].name
|
||||
modpol.interactions.org_dashboard(user, sel_org)
|
||||
else
|
||||
print("Org id not found.")
|
||||
end
|
||||
if sel == "O" or sel == "o" then
|
||||
print('Access which org id?')
|
||||
sel = io.read()
|
||||
print()
|
||||
if modpol.orgs.array[tonumber(sel)] then
|
||||
local sel_org = modpol.orgs.array[tonumber(sel)].name
|
||||
modpol.interactions.org_dashboard(user, sel_org)
|
||||
else
|
||||
print("Org id not found")
|
||||
modpol.interactions.dashboard(user)
|
||||
end
|
||||
|
||||
elseif sel == "U" or sel == "u" then
|
||||
print("Access which user?")
|
||||
sel = io.read()
|
||||
print()
|
||||
if modpol.instance:has_member(sel) then
|
||||
modpol.interactions.user_dashboard(
|
||||
user, sel,
|
||||
function()
|
||||
modpol.interactions.dashboard(user)
|
||||
end
|
||||
)
|
||||
else
|
||||
print("User name not found")
|
||||
modpol.interactions.dashboard(user)
|
||||
end
|
||||
|
||||
elseif sel == "R" or sel == "r" then
|
||||
modpol.instance.members = {}
|
||||
modpol.orgs.reset()
|
||||
print("Orgs and users reset")
|
||||
modpol.interactions.dashboard(user)
|
||||
|
||||
elseif sel == "Q" or "q" then
|
||||
return
|
||||
|
||||
else
|
||||
print("Invalid input, try again")
|
||||
modpol.interactions.dashboard(user)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Output: Displays a menu of org-specific commands to the user
|
||||
-- @function modpol.interactions.org_dashboard
|
||||
-- @param user (string)
|
||||
@ -109,7 +146,7 @@ function modpol.interactions.org_dashboard(user, org_string)
|
||||
print("Org: " .. org.name)
|
||||
print("Parent: " .. parent)
|
||||
print("Members: " .. table.concat(org.members, ", "))
|
||||
print("Children: " .. table.concat(children, ", "))
|
||||
print("Child orgs: " .. table.concat(children, ", "))
|
||||
print("Modules: " .. table.concat(modules, ", "))
|
||||
print("Pending: " .. process_msg)
|
||||
print()
|
||||
@ -132,25 +169,33 @@ function modpol.interactions.org_dashboard(user, org_string)
|
||||
org:call_module(module_sel, user)
|
||||
else
|
||||
print("Error: Module not found.")
|
||||
modpol.interactions.org_dashboard(user, org.id)
|
||||
end
|
||||
|
||||
elseif sel == 'p' or sel == 'P' then
|
||||
local processes = {}
|
||||
print("All processes: (* indicates pending)")
|
||||
for i,v in ipairs(org.processes) do
|
||||
local active = ''
|
||||
if org.pending[user] then
|
||||
if org.pending[user][v.id] then
|
||||
active = '*'
|
||||
if v ~= "deleted" then
|
||||
local active = ''
|
||||
if org.pending[user] then
|
||||
if org.pending[user][v.id] then
|
||||
active = '*'
|
||||
end
|
||||
end
|
||||
print("["..v.id.."] "..v.slug..active)
|
||||
end
|
||||
print("["..v.id.."] "..v.slug..active)
|
||||
end
|
||||
print()
|
||||
print("Interact with which one (use [id] number)?")
|
||||
local to_interact = io.read()
|
||||
local process = org.processes[tonumber(to_interact)]
|
||||
if not process then return end
|
||||
if not process then
|
||||
modpol.interactions.message(
|
||||
user, "Not a pending process")
|
||||
modpol.interactions.org_dashboard(user, org.id)
|
||||
return
|
||||
end
|
||||
if org:has_pending_actions(user) then
|
||||
if org.pending[user][process.id] then
|
||||
org:interact(process.id, user)
|
||||
@ -164,13 +209,46 @@ function modpol.interactions.org_dashboard(user, org_string)
|
||||
end
|
||||
end
|
||||
|
||||
-- Function: modpol.interactions.policy_dashboard
|
||||
-- input: user (string), org_id (int), policy (string)
|
||||
-- if policy is nil, enables creating a new policy
|
||||
-- output: opens a dashboard for viewing/editing policy details
|
||||
-- TODO
|
||||
--- Function: modpol.interactions.user_dashboard
|
||||
-- Displays a dashboard about a particular user
|
||||
-- @param viewer Name of user viewing the dashboard (string)
|
||||
-- @param user Name of user being viewed (string)
|
||||
-- @param completion Optional function to call on Done button
|
||||
function modpol.interactions.user_dashboard(viewer, user, completion)
|
||||
local user_orgs = {}
|
||||
local user_modules = {}
|
||||
|
||||
--- Output: Prints message to CLI
|
||||
print("\n-=< USER DASHBOARD: "..user.." >=-")
|
||||
print("User's orgs:")
|
||||
for id, org in ipairs(modpol.orgs.array) do
|
||||
if type(org) == "table" then
|
||||
if org:has_member(user) then
|
||||
print(org.name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
print()
|
||||
print("Commands: (M)essage user, Enter when done")
|
||||
local sel = io.read()
|
||||
|
||||
if sel == "M" or sel == "m" then
|
||||
modpol.interactions.message_user(
|
||||
viewer, user)
|
||||
completion()
|
||||
else
|
||||
completion()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
-- INTERACTION PRIMITIVES
|
||||
-- ======================
|
||||
|
||||
--- Prints message to CLI.
|
||||
-- Buttons: message, done
|
||||
-- @function modpol.interactions.message
|
||||
-- @param user (string)
|
||||
-- @param message (string)
|
||||
@ -178,11 +256,56 @@ function modpol.interactions.message(user, message)
|
||||
print(user .. ": " .. message)
|
||||
end
|
||||
|
||||
--- Output: Applies "func" to user input.
|
||||
--- Function: modpol.interactions.message_user
|
||||
-- Gets and sends a message from one user to another
|
||||
-- @param sender Name of user sending (string)
|
||||
-- @param recipient Name of user receiving (string)
|
||||
function modpol.interactions.message_user(sender, recipient)
|
||||
print("Enter your message for "..recipient..":")
|
||||
local sel = io.read()
|
||||
modpol.interactions.message(
|
||||
recipient,
|
||||
sel.." [from "..sender.."]")
|
||||
end
|
||||
|
||||
--- Function: modpol.interactions.display
|
||||
-- Displays complex data to a user
|
||||
-- @param user Name of target user (string)
|
||||
-- @param title Title of display (string)
|
||||
-- @param message Content of message (string or table of strings)
|
||||
-- @param done Optional function for what happens when user is done
|
||||
function modpol.interactions.display(user, title, message, completion)
|
||||
local output = ""
|
||||
output = "\n-=< "..title.." >=-\n\n"
|
||||
if type(message) == "table" then
|
||||
output = table.concat(message,"\n")
|
||||
elseif type(message) == "string" then
|
||||
output = message
|
||||
elseif type(message) == "number" then
|
||||
output = message
|
||||
else
|
||||
modpol.interactions.message(
|
||||
self.initiator, "Error: message not typed for display")
|
||||
modpol.interactions.message(
|
||||
self.initiator, "Error: input not typed for display")
|
||||
if completion then completion() else
|
||||
modpol.intereactions.dashboard(user)
|
||||
end
|
||||
end
|
||||
print(message)
|
||||
print("\nEnter to continue")
|
||||
io.read()
|
||||
if completion then completion() else
|
||||
modpol.intereactions.dashboard(user)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Applies "func" to user input
|
||||
-- Func input: user input (string)
|
||||
-- @function modpol.interactions.text_query
|
||||
-- @param user (string)
|
||||
-- @param query (string)
|
||||
-- @param User (string)
|
||||
-- @param Query (string)
|
||||
-- @param func (function)
|
||||
function modpol.interactions.text_query(user, query, func)
|
||||
print(user .. ": " .. query)
|
||||
@ -197,8 +320,6 @@ end
|
||||
-- @param label (string)
|
||||
-- @param options (table of strings)
|
||||
-- @param func (choice) (function)
|
||||
|
||||
|
||||
function modpol.interactions.dropdown_query(user, label, options, func)
|
||||
-- set up options
|
||||
local options_display = ""
|
||||
@ -211,7 +332,7 @@ function modpol.interactions.dropdown_query(user, label, options, func)
|
||||
options_display = options_display .. "Select number:"
|
||||
if options_number == 0 then
|
||||
print("Error: No options given for dropdown")
|
||||
return nil
|
||||
return nil
|
||||
end
|
||||
-- begin displaying
|
||||
print(user .. ": " .. label)
|
||||
@ -234,12 +355,60 @@ function modpol.interactions.dropdown_query(user, label, options, func)
|
||||
end
|
||||
end
|
||||
|
||||
--- Output: Applies "func" to user input.
|
||||
-- Func input: user input (string: y/n)
|
||||
-- @function modpol.binary_poll_user(user, question)
|
||||
-- @param user (string)
|
||||
-- @param question (string)
|
||||
-- @param func (function)
|
||||
|
||||
--- Allows user to select from a set of options
|
||||
-- @function modpol.interactions.checkbox_query
|
||||
-- @param user Name of user (string)
|
||||
-- @param label Query for user before options (string)
|
||||
-- @param options Table of options and their checked status in the form {{"option_1_string", true}, {"option_2_string", false}}
|
||||
-- @param func Function to be called with param "input", made up of the corrected table in the same format as the param options
|
||||
function modpol.interactions.checkbox_query(
|
||||
user, label, options, func)
|
||||
-- set up options
|
||||
local options_display = ""
|
||||
local options_number = 0
|
||||
for i,v in ipairs(options) do
|
||||
local checked = false
|
||||
if v[2] then checked = true end
|
||||
if checked then
|
||||
checked = "x"
|
||||
else
|
||||
checked = " "
|
||||
end
|
||||
options_display = options_display..i..". ["..
|
||||
checked.."] "..v[1].."\n"
|
||||
options_number = options_number + 1
|
||||
end
|
||||
if options_number == 0 then
|
||||
print("Error: No options given for dropdown")
|
||||
return nil
|
||||
end
|
||||
options_display = options_display..
|
||||
"List comma-separated options to flip (e.g., 1,2,5):"
|
||||
-- begin displaying
|
||||
print(user .. ": " .. label)
|
||||
print(options_display)
|
||||
-- read input and produce output
|
||||
local answer = io.read()
|
||||
local answer_table = {}
|
||||
for match in (answer..","):gmatch("(.-)"..",") do
|
||||
table.insert(answer_table, tonumber(match))
|
||||
end
|
||||
local result_table = modpol.util.copy_table(options)
|
||||
for i,v in ipairs(answer_table) do
|
||||
if result_table[v] then
|
||||
-- flip the boolean on selected options
|
||||
result_table[v][2] = not result_table[v][2]
|
||||
end
|
||||
end
|
||||
func(result_table)
|
||||
end
|
||||
|
||||
|
||||
-- Function: modpol.interactions.binary_poll_user
|
||||
-- Params: user (string), question (string), func (function)
|
||||
-- func input: user input (string: y/n)
|
||||
-- Output: Applies "func" to user input
|
||||
function modpol.interactions.binary_poll_user(user, question, func)
|
||||
local query = "Poll for " .. user .. " (y/n): ".. question
|
||||
local answer
|
||||
@ -280,3 +449,9 @@ end
|
||||
-- output: gets question from initiator, asks all org members, broadcasts answers
|
||||
|
||||
|
||||
-- TESTING
|
||||
|
||||
--testing command
|
||||
function modpol.msg(text)
|
||||
modpol.interactions.message("TEST MSG",text)
|
||||
end
|
||||
|
@ -28,7 +28,6 @@ function add_child_org_consent:initiate(result)
|
||||
modpol.interactions.org_dashboard(
|
||||
self.initiator, self.org.name)
|
||||
self.org:delete_process(self.id)
|
||||
if result then result() end
|
||||
return
|
||||
elseif modpol.orgs.get_org(input) then
|
||||
modpol.interactions.message(
|
||||
@ -45,7 +44,7 @@ function add_child_org_consent:initiate(result)
|
||||
self.initiator,
|
||||
"Proposed child org: " .. input)
|
||||
-- initiate consent process
|
||||
self.org:call_module(
|
||||
self:call_module(
|
||||
"consent",
|
||||
self.initiator,
|
||||
{
|
||||
@ -70,7 +69,8 @@ function add_child_org_consent:create_child_org()
|
||||
modpol.interactions.message_org(
|
||||
self.initiator,
|
||||
self.org.name,
|
||||
"Child org created: "..self.data.child_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
|
||||
|
169
modpol_core/modules/change_modules-dropdown.lua
Normal file
169
modpol_core/modules/change_modules-dropdown.lua
Normal file
@ -0,0 +1,169 @@
|
||||
--- change_modules
|
||||
-- @module change_modules
|
||||
-- Depends on consent
|
||||
|
||||
local change_modules = {
|
||||
name = "Change modules (consent)",
|
||||
slug = "change_modules",
|
||||
desc = "Add or remove modules from the org with member consent",
|
||||
hide = false;
|
||||
}
|
||||
|
||||
change_modules.data = {
|
||||
result = nil
|
||||
}
|
||||
|
||||
change_modules.config = {
|
||||
}
|
||||
|
||||
function change_modules:initiate(result)
|
||||
self.data.result = result
|
||||
-- Step 1: add or remove?
|
||||
modpol.interactions.dropdown_query(
|
||||
self.initiator, "Module change options:",
|
||||
{"Add module","Remove module"},
|
||||
function(input)
|
||||
if input == "Add module" then
|
||||
self:add_module()
|
||||
elseif input == "Remove module" then
|
||||
self:remove_module()
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
function change_modules:add_module()
|
||||
-- prepare module options
|
||||
local available_modules = modpol.util.copy_table(modpol.modules)
|
||||
for k,org_mod in pairs(self.org.modules) do
|
||||
if available_modules[org_mod.slug] then
|
||||
available_modules[org_mod.slug] = nil
|
||||
end end
|
||||
-- present module options
|
||||
local modules_list = {}
|
||||
for k,v in pairs(available_modules) do
|
||||
table.insert(modules_list,v.name)
|
||||
end
|
||||
if #modules_list == 0 then
|
||||
modpol.interactions.message(
|
||||
self.initiator, "Org has all modules")
|
||||
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
|
||||
end
|
||||
table.sort(modules_list)
|
||||
-- now ask which to add
|
||||
modpol.interactions.dropdown_query(
|
||||
self.initiator, "Choose a module to add:",
|
||||
modules_list,
|
||||
function(mod_choice)
|
||||
-- confirm choice
|
||||
modpol.interactions.binary_poll_user(
|
||||
self.initiator,
|
||||
"Confirm: propose to add module \"" ..
|
||||
mod_choice .. "\"?",
|
||||
function(input)
|
||||
if input == "Yes" then
|
||||
self:propose_change("add",mod_choice)
|
||||
modpol.interactions.org_dashboard(
|
||||
self.initiator, self.org.id)
|
||||
else
|
||||
self:add_module()
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
function change_modules:remove_module()
|
||||
-- prepare module options
|
||||
local available_modules = {}
|
||||
for k,org_mod in pairs(self.org.modules) do
|
||||
if not org_mod.hide then
|
||||
available_modules[org_mod.slug] = modpol.util.copy_table(org_mod)
|
||||
end end
|
||||
local modules_list = {}
|
||||
local modules_count = 0
|
||||
for k,v in pairs(available_modules) do
|
||||
table.insert(modules_list,v.name)
|
||||
modules_count = modules_count + 1
|
||||
end
|
||||
-- abort if no modules to remove
|
||||
if modules_count == 0 then
|
||||
modpol.interactions.message(
|
||||
self.initiator, "Org has no modules")
|
||||
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
|
||||
end
|
||||
table.sort(modules_list)
|
||||
-- now ask which to remove
|
||||
modpol.interactions.dropdown_query(
|
||||
self.initiator, "Choose a module to remove:",
|
||||
modules_list,
|
||||
function(mod_choice)
|
||||
-- confirm choice
|
||||
modpol.interactions.binary_poll_user(
|
||||
self.initiator,
|
||||
"Confirm: propose to remove module \"" .. mod_choice .. "\"?",
|
||||
function(input)
|
||||
if input == "Yes" then
|
||||
self:propose_change("remove",mod_choice)
|
||||
modpol.interactions.org_dashboard(
|
||||
self.initiator, self.org.id)
|
||||
else
|
||||
self:remove_module()
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
--- propose_change
|
||||
-- @field type "add" or "remove"
|
||||
function change_modules:propose_change(type, mod_text)
|
||||
self:call_module(
|
||||
"consent",self.initiator,
|
||||
{
|
||||
prompt = "Do you consent to "..type..
|
||||
" this module in org "..self.org.name..
|
||||
":\n"..mod_text,
|
||||
votes_required = #self.org.members
|
||||
},
|
||||
function()
|
||||
if type == "add" then
|
||||
for k,v in pairs(modpol.modules) do
|
||||
if v.name == mod_text then
|
||||
table.insert(self.org.modules,v)
|
||||
end
|
||||
end
|
||||
modpol.interactions.message_org(
|
||||
self.initiator,self.org.id,
|
||||
"Consent reached:\nAdding \""
|
||||
..mod_text.."\" to org "..self.org.name)
|
||||
elseif type == "remove" then
|
||||
local i = 0
|
||||
for k,v in pairs(self.org.modules) do
|
||||
i = i + 1
|
||||
if v.name == mod_text then
|
||||
self.org.modules[k] = nil
|
||||
end
|
||||
end
|
||||
modpol.interactions.message_org(
|
||||
self.initiator,self.org.id,
|
||||
"Consent reached:\nRemoving \""
|
||||
..mod_text.."\" from org "..self.org.name)
|
||||
end
|
||||
end)
|
||||
if self.data.result then self.data.result() end
|
||||
self.org:delete_process(self.id)
|
||||
end
|
||||
|
||||
--- (Required) Add to module table
|
||||
modpol.modules.change_modules = change_modules
|
@ -10,7 +10,10 @@ local change_modules = {
|
||||
}
|
||||
|
||||
change_modules.data = {
|
||||
result = nil
|
||||
result = nil,
|
||||
modules_before = {},
|
||||
modules_after = {},
|
||||
summary = "",
|
||||
}
|
||||
|
||||
change_modules.config = {
|
||||
@ -21,151 +24,93 @@ change_modules.config = {
|
||||
-- @function change_modules:initiate
|
||||
function change_modules:initiate(result)
|
||||
self.data.result = result
|
||||
-- Step 1: add or remove?
|
||||
modpol.interactions.dropdown_query(
|
||||
self.initiator, "Module change options:",
|
||||
{"Add module","Remove module"},
|
||||
self.data.add_modules = {}
|
||||
self.data.remove_modules = {}
|
||||
local modules_before = {}
|
||||
local modules_after = {}
|
||||
-- generate self.config.modules table
|
||||
for k, module in pairs(modpol.modules) do
|
||||
if not modpol.modules[module.slug].hide then
|
||||
local in_org = false
|
||||
if self.org.modules[module.slug] then
|
||||
in_org = true
|
||||
end
|
||||
table.insert(
|
||||
modules_before,
|
||||
{module.name.." ["..module.slug.."]", in_org})
|
||||
end
|
||||
end
|
||||
-- send query to user
|
||||
modpol.interactions.checkbox_query(
|
||||
self.initiator,
|
||||
"Check the modules to activate in this org:",
|
||||
modules_before,
|
||||
function(input)
|
||||
if input == "Add module" then
|
||||
self:add_module()
|
||||
elseif input == "Remove module" then
|
||||
self:remove_module()
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
function change_modules:add_module()
|
||||
-- prepare module options
|
||||
local available_modules = modpol.util.copy_table(modpol.modules)
|
||||
for k,org_mod in pairs(self.org.modules) do
|
||||
if available_modules[org_mod.slug] then
|
||||
available_modules[org_mod.slug] = nil
|
||||
end end
|
||||
-- present module options
|
||||
local modules_list = {}
|
||||
for k,v in pairs(available_modules) do
|
||||
table.insert(modules_list,v.name)
|
||||
end
|
||||
if #modules_list == 0 then
|
||||
modpol.interactions.message(
|
||||
self.initiator, "Org has all modules")
|
||||
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
|
||||
end
|
||||
table.sort(modules_list)
|
||||
-- now ask which to add
|
||||
modpol.interactions.dropdown_query(
|
||||
self.initiator, "Choose a module to add:",
|
||||
modules_list,
|
||||
function(mod_choice)
|
||||
-- confirm choice
|
||||
modpol.interactions.binary_poll_user(
|
||||
self.initiator,
|
||||
"Confirm: propose to add module \"" ..
|
||||
mod_choice .. "\"?",
|
||||
function(input)
|
||||
if input == "Yes" then
|
||||
self:propose_change("add",mod_choice)
|
||||
modpol.interactions.org_dashboard(
|
||||
self.initiator, self.org.id)
|
||||
-- identify changes
|
||||
modules_after = input
|
||||
for i,v in ipairs(modules_after) do
|
||||
if v[2] ~= modules_before[i][2] then
|
||||
if v[2] then
|
||||
table.insert(self.data.add_modules, v[1])
|
||||
else
|
||||
self:add_module()
|
||||
table.insert(self.data.remove_modules, v[1])
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
function change_modules:remove_module()
|
||||
-- prepare module options
|
||||
local available_modules = {}
|
||||
for k,org_mod in pairs(self.org.modules) do
|
||||
if not org_mod.hide then
|
||||
available_modules[org_mod.slug] = modpol.copy_table(org_mod)
|
||||
end end
|
||||
local modules_list = {}
|
||||
local modules_count = 0
|
||||
for k,v in pairs(available_modules) do
|
||||
table.insert(modules_list,v.name)
|
||||
modules_count = modules_count + 1
|
||||
end
|
||||
-- abort if no modules to remove
|
||||
if modules_count == 0 then
|
||||
modpol.interactions.message(
|
||||
self.initiator, "Org has no modules")
|
||||
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
|
||||
end
|
||||
table.sort(modules_list)
|
||||
-- now ask which to remove
|
||||
modpol.interactions.dropdown_query(
|
||||
self.initiator, "Choose a module to remove:",
|
||||
modules_list,
|
||||
function(mod_choice)
|
||||
-- confirm choice
|
||||
modpol.interactions.binary_poll_user(
|
||||
self.initiator,
|
||||
"Confirm: propose to remove module \"" .. mod_choice .. "\"?",
|
||||
function(input)
|
||||
if input == "Yes" then
|
||||
self:propose_change("remove",mod_choice)
|
||||
modpol.interactions.org_dashboard(
|
||||
self.initiator, self.org.id)
|
||||
else
|
||||
self:remove_module()
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
--- propose_change
|
||||
-- @field type "add" or "remove"
|
||||
function change_modules:propose_change(type, mod_text)
|
||||
self.org:call_module(
|
||||
"consent",self.initiator,
|
||||
{
|
||||
prompt = "Do you consent to "..type..
|
||||
" this module in org "..self.org.name..
|
||||
":\n"..mod_text,
|
||||
votes_required = #self.org.members
|
||||
},
|
||||
function()
|
||||
if type == "add" then
|
||||
for k,v in pairs(modpol.modules) do
|
||||
if v.name == mod_text then
|
||||
table.insert(self.org.modules,v)
|
||||
end
|
||||
end
|
||||
modpol.interactions.message_org(
|
||||
self.initiator,self.org.id,
|
||||
"Consent reached:\nAdding \""
|
||||
..mod_text.."\" to org "..self.org.name)
|
||||
elseif type == "remove" then
|
||||
modpol.msg("Removing!")
|
||||
local i = 0
|
||||
for k,v in pairs(self.org.modules) do
|
||||
i = i + 1
|
||||
if v.name == mod_text then
|
||||
modpol.msg("got it!")
|
||||
self.org.modules[k] = nil
|
||||
end
|
||||
end
|
||||
modpol.interactions.message_org(
|
||||
self.initiator,self.org.id,
|
||||
"Consent reached:\nRemoving \""
|
||||
..mod_text.."\" from org "..self.org.name)
|
||||
end
|
||||
-- abort if no changes
|
||||
if #self.data.add_modules == 0
|
||||
and #self.data.remove_modules == 0 then
|
||||
modpol.interactions.message(
|
||||
self.initiator, "No module changes proposed")
|
||||
modpol.interactions.org_dashboard(
|
||||
self.initiator, self.org.id)
|
||||
self.org:delete_process(self.id)
|
||||
return
|
||||
end
|
||||
-- proceed with consent
|
||||
local query = "Accept module changes in org "..
|
||||
self.org.name.."?"
|
||||
self.data.summary = ""
|
||||
if #self.data.add_modules > 0 then
|
||||
self.data.summary = self.data.summary.."\nAdd: "..
|
||||
table.concat(self.data.add_modules,", ")
|
||||
elseif #self.data.remove_modules > 0 then
|
||||
self.data.summary = "\nRemove: "..
|
||||
table.concat(self.data.remove_modules,", ")
|
||||
end
|
||||
self:call_module(
|
||||
"consent",
|
||||
self.initiator,
|
||||
{
|
||||
prompt = query..self.data.summary,
|
||||
votes_required = #self.org.members
|
||||
},
|
||||
function()
|
||||
self:implement_change()
|
||||
end)
|
||||
modpol.interactions.org_dashboard(
|
||||
self.initiator, self.org.id)
|
||||
end)
|
||||
end
|
||||
|
||||
function change_modules:implement_change()
|
||||
for i,v in ipairs(self.data.add_modules) do
|
||||
local slug = string.match(v,"%[(.+)%]")
|
||||
self.org.modules[slug] =
|
||||
modpol.util.copy_table(modpol.modules[slug])
|
||||
table.sort(self.org.modules)
|
||||
end
|
||||
for i,v in ipairs(self.data.remove_modules) do
|
||||
local slug = string.match(v,"%[(.+)%]")
|
||||
self.org.modules[slug] = nil
|
||||
table.sort(self.org.modules)
|
||||
end
|
||||
-- announce and shut down
|
||||
modpol.interactions.message_org(
|
||||
self.initiator,
|
||||
self.org.id,
|
||||
"Module changes applied to org "..self.org.name..":"..
|
||||
self.data.summary)
|
||||
if self.data.result then self.data.result() end
|
||||
self.org:delete_process(self.id)
|
||||
end
|
||||
|
@ -2,9 +2,9 @@
|
||||
-- @module consent
|
||||
|
||||
local consent = {
|
||||
name = "Consent process utility",
|
||||
name = "Consent process",
|
||||
slug = "consent",
|
||||
desc = "A module other modules use for consent decisions",
|
||||
desc = "A utility module other modules use for consent decisions",
|
||||
hide = true
|
||||
}
|
||||
|
||||
@ -26,7 +26,7 @@ function consent:initiate(result)
|
||||
if self.org:get_member_count() == 0 then
|
||||
if self.data.result then
|
||||
self.data.result() end
|
||||
self.org:wipe_pending_actions(self.id)
|
||||
self.org:delete_process(self.id)
|
||||
else
|
||||
-- otherwise, create poll
|
||||
for id, member in pairs(self.org.members) do
|
||||
@ -47,14 +47,19 @@ function consent:callback(member)
|
||||
if resp == "Yes" then
|
||||
self.data.votes = self.data.votes + 1
|
||||
end
|
||||
modpol.interactions.message_org(
|
||||
"consent", self.org.id,
|
||||
member.." decided "..resp.." on: "..
|
||||
self.config.prompt.." ("..self.data.votes..
|
||||
"/"..self.config.votes_required..")"
|
||||
)
|
||||
if self.data.votes >= self.config.votes_required then
|
||||
if self.data.result then
|
||||
self.data.result() end
|
||||
self.org:wipe_pending_actions(self.id)
|
||||
self.org:delete_process(self.id)
|
||||
end
|
||||
modpol.interactions.org_dashboard(
|
||||
member, self.org.name)
|
||||
member, self.org.id)
|
||||
end
|
||||
)
|
||||
end
|
||||
|
53
modpol_core/modules/create_token.lua
Normal file
53
modpol_core/modules/create_token.lua
Normal file
@ -0,0 +1,53 @@
|
||||
--- create_token
|
||||
-- @module create_token
|
||||
-- depends on tokenomics
|
||||
|
||||
local create_token = {
|
||||
name = "Create a token (consent)",
|
||||
slug = "create_token",
|
||||
desc = "With org consent, creates an org token",
|
||||
hide = false;
|
||||
}
|
||||
|
||||
--- (Required) Data for module
|
||||
-- Variables that module uses during the course of a process
|
||||
-- Can be blank
|
||||
create_token.data = {
|
||||
}
|
||||
|
||||
create_token.config = {
|
||||
token_name = ""
|
||||
}
|
||||
|
||||
--- (Required): initiate function
|
||||
-- @param result (optional) Callback if this module is embedded in other modules
|
||||
-- @function initiate
|
||||
function create_token:initiate(result)
|
||||
modpol.interactions.text_query(
|
||||
self.initiator,
|
||||
"Token name (alpha-numeric, no spaces):",
|
||||
function(input)
|
||||
self.config.token_name = input
|
||||
self:call_module(
|
||||
"tokenomics",
|
||||
self.initiator,
|
||||
{
|
||||
consent = true,
|
||||
token_slug = self.config.token_name
|
||||
},
|
||||
function(input2)
|
||||
modpol.interactions.org_dashboard(
|
||||
self.initiator, self.org.name)
|
||||
if result then result() end
|
||||
-- call this wherever process might end:
|
||||
self.org:delete_process(self.id)
|
||||
end
|
||||
)
|
||||
modpol.interactions.org_dashboard(
|
||||
self.initiator, self.org.name)
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
--- (Required) Add to module table
|
||||
modpol.modules.create_token = create_token
|
57
modpol_core/modules/defer_consent.lua
Normal file
57
modpol_core/modules/defer_consent.lua
Normal file
@ -0,0 +1,57 @@
|
||||
--- defer_consent
|
||||
-- @module defer_consent
|
||||
|
||||
--- (Required): data table containing name and description of the module
|
||||
-- @field name "Human-readable name (parens OK, no brackets)"
|
||||
-- @field slug "Same as module class name"
|
||||
-- @field desc "Description of the module"
|
||||
-- @field hide "Whether this is a hidden utility module"
|
||||
local defer_consent = {
|
||||
name = "Defer consent",
|
||||
slug = "defer_consent",
|
||||
desc = "Defers consent on a decision to another org",
|
||||
hide = true;
|
||||
}
|
||||
|
||||
--- (Required) Data for module
|
||||
-- Variables that module uses during the course of a process
|
||||
-- Can be blank
|
||||
defer_consent.data = {
|
||||
}
|
||||
|
||||
--- (Required): 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 = {
|
||||
defer_org = "Root",
|
||||
votes_required = 1,
|
||||
prompt = "Do you consent?"
|
||||
}
|
||||
|
||||
--- (Required): initiate function
|
||||
-- @param result (optional) Callback if this module is embedded in other modules
|
||||
-- @function initiate
|
||||
function defer_consent:initiate(result)
|
||||
local defer_org = modpol.orgs.get_org(self.config.defer_org)
|
||||
if not defer_org then
|
||||
modpol.interactions.message(
|
||||
self.initiator, "Target org not found, aborting")
|
||||
self.org:delete_process(self.id)
|
||||
else
|
||||
defer_org:call_module(
|
||||
"consent", self.initiator,
|
||||
{
|
||||
votes_required = self.config.votes_required,
|
||||
prompt = self.config.prompt
|
||||
},
|
||||
function()
|
||||
if result then result() end
|
||||
end)
|
||||
end
|
||||
if result then result() end
|
||||
self.org:delete_process(self.id)
|
||||
end
|
||||
|
||||
--- (Required) Add to module table
|
||||
modpol.modules.defer_consent = defer_consent
|
70
modpol_core/modules/display_processes.lua
Normal file
70
modpol_core/modules/display_processes.lua
Normal file
@ -0,0 +1,70 @@
|
||||
--- display_processes
|
||||
-- @module display_processes
|
||||
|
||||
local display_processes = {
|
||||
name = "Display processes",
|
||||
slug = "display_processes",
|
||||
desc = "Presents a detailed list of org processes",
|
||||
hide = false;
|
||||
}
|
||||
|
||||
--- (Required) Data for module
|
||||
-- Variables that module uses during the course of a process
|
||||
-- Can be blank
|
||||
display_processes.data = {
|
||||
}
|
||||
|
||||
display_processes.config = {
|
||||
}
|
||||
|
||||
--- (Required): initiate function
|
||||
-- @param result (optional) Callback if this module is embedded in other modules
|
||||
-- @function initiate
|
||||
function display_processes:initiate(result)
|
||||
local display_table = {}
|
||||
for k,v in pairs(self.org.processes) do
|
||||
if v ~= "deleted" then
|
||||
local input = v.id..": "..v.slug
|
||||
table.insert(display_table, input)
|
||||
input = "Org: "..v.org.name..
|
||||
", initiator: "..v.initiator
|
||||
table.insert(display_table, input)
|
||||
if v.config
|
||||
and modpol.util.num_pairs(v.config) > 0 then
|
||||
table.insert(display_table, "Policies:")
|
||||
for k2,v2 in pairs(v.config) do
|
||||
local v2_string = ""
|
||||
if type(v2) ~= "string"
|
||||
and type(v2) ~= "table" then
|
||||
v2_string = tostring(v2)
|
||||
elseif type(v2) == "table" then
|
||||
v2_string = tostring(v2)
|
||||
else
|
||||
v2_string = "Could not render"
|
||||
end
|
||||
input = k2..": "..v2_string
|
||||
table.insert(display_table, input)
|
||||
end
|
||||
end
|
||||
table.insert(display_table, "\n")
|
||||
end
|
||||
end
|
||||
local output = table.concat(display_table,"\n")
|
||||
if #display_table == 0 then
|
||||
output = "No processes found"
|
||||
end
|
||||
modpol.interactions.display(
|
||||
self.initiator,
|
||||
"Processes in org "..self.org.name,
|
||||
output,
|
||||
function()
|
||||
modpol.interactions.org_dashboard(
|
||||
self.initiator, self.org.id)
|
||||
if result then result() end
|
||||
self.org:delete_process(self.id)
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
--- (Required) Add to module table
|
||||
modpol.modules.display_processes = display_processes
|
@ -28,7 +28,7 @@ function join_org_consent:initiate(result)
|
||||
self.org:delete_process(self.id)
|
||||
else
|
||||
self.data.result = result
|
||||
self.org:call_module(
|
||||
self:call_module(
|
||||
"consent",
|
||||
self.initiator,
|
||||
{
|
||||
|
49
modpol_core/modules/randomizer.lua
Normal file
49
modpol_core/modules/randomizer.lua
Normal file
@ -0,0 +1,49 @@
|
||||
--- @module randomizer
|
||||
-- A utility module that outputs a random result from a set of options
|
||||
|
||||
local randomizer = {
|
||||
name = "Randomizer",
|
||||
slug = "randomizer",
|
||||
desc = "A utility module other modules use for random decisions",
|
||||
hide = true
|
||||
}
|
||||
|
||||
randomizer.data = {
|
||||
}
|
||||
|
||||
-- options_table should be a table of strings
|
||||
randomizer.config = {
|
||||
options_table = {},
|
||||
num_results = 1,
|
||||
result_table = {}
|
||||
}
|
||||
|
||||
function randomizer:initiate(result)
|
||||
self.data.result = result
|
||||
self.data.options_table = modpol.util.copy_table(self.config.options_table)
|
||||
-- if options table is empty, randomizer returns that
|
||||
if #self.data.options_table == 0 or self.config.num_results == 0 then
|
||||
if self.data.result then
|
||||
self.data.result({}) end
|
||||
self.org:delete_process(self.id)
|
||||
else
|
||||
-- otherwise, choose a random result
|
||||
self.random_loop()
|
||||
end
|
||||
end
|
||||
|
||||
-- returns result_table
|
||||
function randomizer:random_loop()
|
||||
self.data.results = 0
|
||||
if results == self.config.num_results then
|
||||
self.data.result(self.data.result_table)
|
||||
else
|
||||
math.randomseed(os.time())
|
||||
local index = math.random(self.data.options_table)
|
||||
table.insert(self.data.result_table, self.data.options_table[index])
|
||||
table.remove(self.data.options_table, index)
|
||||
self.data.results = self.data.results + 1
|
||||
end
|
||||
end
|
||||
|
||||
modpol.modules.randomizer = randomizer
|
@ -42,7 +42,7 @@ function remove_child_consent:initiate(result)
|
||||
children,
|
||||
function(input)
|
||||
self.data.child_to_remove = modpol.orgs.get_org(input)
|
||||
self.org:call_module(
|
||||
self:call_module(
|
||||
"consent",
|
||||
self.initiator,
|
||||
{
|
||||
|
@ -35,7 +35,7 @@ function remove_member_consent:initiate(result)
|
||||
self.org.members,
|
||||
function(input)
|
||||
self.data.member_to_remove = input
|
||||
self.org:call_module(
|
||||
self:call_module(
|
||||
"consent",
|
||||
self.initiator,
|
||||
{
|
||||
|
@ -28,14 +28,14 @@ function remove_org_consent:initiate(result)
|
||||
self.org:delete_process(self.id)
|
||||
else
|
||||
self.data.result = result
|
||||
self.org:call_module(
|
||||
self:call_module(
|
||||
"consent",
|
||||
self.initiator,
|
||||
{
|
||||
prompt = "Remove org " .. self.org.name .. "?",
|
||||
votes_required = #self.org.members
|
||||
},
|
||||
function ()
|
||||
function()
|
||||
self:complete()
|
||||
end
|
||||
)
|
||||
|
113
modpol_core/modules/remove_process.lua
Normal file
113
modpol_core/modules/remove_process.lua
Normal file
@ -0,0 +1,113 @@
|
||||
--- remove_process
|
||||
-- @module remove_process
|
||||
|
||||
local remove_process = {
|
||||
name = "Remove process",
|
||||
slug = "remove_process",
|
||||
desc = "User can remove own processes, consent required for those of others",
|
||||
hide = false;
|
||||
}
|
||||
|
||||
--- (Required) Data for module
|
||||
-- Variables that module uses during the course of a process
|
||||
-- Can be blank
|
||||
remove_process.data = {
|
||||
}
|
||||
|
||||
remove_process.config = {
|
||||
}
|
||||
|
||||
--- (Required): initiate function
|
||||
-- @param result (optional) Callback if this module is embedded in other modules
|
||||
-- @function initiate
|
||||
function remove_process:initiate(result)
|
||||
-- prepare process options
|
||||
local available_processes = {}
|
||||
for k,process in pairs(self.org.processes) do
|
||||
if process ~= "deleted" then
|
||||
available_processes[process.id] = process
|
||||
end
|
||||
end
|
||||
local process_list = {}
|
||||
local process_count = 0
|
||||
for k,v in pairs(available_processes) do
|
||||
local mine = ""
|
||||
if v.initiator == self.initiator then mine = "*" end
|
||||
table.insert(process_list,"["..v.id.."] "..v.slug..mine)
|
||||
process_count = process_count + 1
|
||||
end
|
||||
-- abort if no processes to remove
|
||||
if process_count == 0 then
|
||||
modpol.interactions.message(
|
||||
self.initiator, "Org has no modules")
|
||||
modpol.interactions.org_dashboard(
|
||||
self.initiator, self.org.id)
|
||||
if result then result() end
|
||||
self.org:delete_process(self.id)
|
||||
return
|
||||
end
|
||||
table.sort(process_list)
|
||||
-- now ask which to remove
|
||||
modpol.interactions.dropdown_query(
|
||||
self.initiator, "Choose a process to remove (* marks yours, no consent required):",
|
||||
process_list,
|
||||
function(process_choice)
|
||||
-- confirm choice
|
||||
local process_id = tonumber(
|
||||
string.match(process_choice, "%d+"))
|
||||
local process_mine = string.match(process_choice,
|
||||
"%*")
|
||||
modpol.interactions.binary_poll_user(
|
||||
self.initiator,
|
||||
"Confirm: Remove process \""..
|
||||
process_choice .. "\"?",
|
||||
function(input)
|
||||
if input == "Yes" then
|
||||
if process_mine then
|
||||
self.org:delete_process_tree(process_id)
|
||||
modpol.interactions.message(
|
||||
self.initiator,
|
||||
"Removed process: "..process_choice)
|
||||
modpol.interactions.org_dashboard(
|
||||
self.initiator, self.org.id)
|
||||
if result then result() end
|
||||
self.org:delete_process(self.id)
|
||||
else
|
||||
self:call_module(
|
||||
"consent",
|
||||
self.initiator,
|
||||
{
|
||||
prompt = "Approve removal of process "..process_choice.."?",
|
||||
votes_required = #self.org.members
|
||||
},
|
||||
function(input)
|
||||
modpol.interactions.message_org(
|
||||
self.initiator,
|
||||
self.org.id,
|
||||
"Removing process: "..
|
||||
process_choice)
|
||||
self.org:delete_process_tree(process_id)
|
||||
modpol.interactions.org_dashboard(
|
||||
self.initiator, self.org.id)
|
||||
if result then result() end
|
||||
self.org:delete_process(self.id)
|
||||
end
|
||||
)
|
||||
end
|
||||
modpol.interactions.org_dashboard(
|
||||
self.initiator, self.org.id)
|
||||
else
|
||||
modpol.interactions.org_dashboard(
|
||||
self.initiator, self.org.id)
|
||||
if result then result() end
|
||||
self.org:delete_process(self.id)
|
||||
end
|
||||
end
|
||||
)
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
--- (Required) Add to module table
|
||||
modpol.modules.remove_process = remove_process
|
@ -49,7 +49,7 @@ function rename_org_consent:initiate(result)
|
||||
"Proposed to change name of org " ..
|
||||
self.org.name .. " to " .. input)
|
||||
-- initiate consent process
|
||||
self.org:call_module(
|
||||
self:call_module(
|
||||
"consent",
|
||||
self.initiator,
|
||||
{
|
||||
|
77
modpol_core/modules/send_token.lua
Normal file
77
modpol_core/modules/send_token.lua
Normal file
@ -0,0 +1,77 @@
|
||||
--- send_token
|
||||
-- @module send_token
|
||||
-- depends on tokenomics
|
||||
|
||||
local send_token = {
|
||||
name = "Send tokens",
|
||||
slug = "send_token",
|
||||
desc = "Send tokens to another user",
|
||||
hide = false;
|
||||
}
|
||||
|
||||
--- (Required) Data for module
|
||||
-- Variables that module uses during the course of a process
|
||||
-- Can be blank
|
||||
send_token.data = {
|
||||
}
|
||||
|
||||
send_token.config = {
|
||||
token_name = ""
|
||||
}
|
||||
|
||||
--- (Required): initiate function
|
||||
-- @param result (optional) Callback if this module is embedded in other modules
|
||||
-- @function initiate
|
||||
function send_token:initiate(result)
|
||||
local token_list = {}
|
||||
if self.org.tokens then
|
||||
for k,v in pairs(self.org.tokens) do
|
||||
table.insert(token_list, k)
|
||||
end
|
||||
end
|
||||
if token_list == {} then
|
||||
modpol.interactions.message(
|
||||
self.initiator,
|
||||
"No tokens in org")
|
||||
modpol.interactions.org_dashboard(
|
||||
self.initiator, self.org.name)
|
||||
self.org:delete_process(self.id)
|
||||
return
|
||||
else
|
||||
modpol.interactions.dropdown_query(
|
||||
self.initiator,
|
||||
"Which token do you want to send?",
|
||||
token_list,
|
||||
function(input_token)
|
||||
modpol.interactions.dropdown_query(
|
||||
self.initiator,
|
||||
"Who do you want to send to?",
|
||||
modpol.util.copy_table(self.org.members),
|
||||
function(input_recipient)
|
||||
modpol.interactions.text_query(
|
||||
self.initiator,
|
||||
"How much do you want to give (a number)?",
|
||||
function(input_amount)
|
||||
modpol.modules.tokenomics.transfer(
|
||||
self.org.id,
|
||||
input_token,
|
||||
self.initiator,
|
||||
input_recipient,
|
||||
input_amount
|
||||
)
|
||||
modpol.interactions.org_dashboard(
|
||||
self.initiator, self.org.name)
|
||||
-- close process
|
||||
if result then result() end
|
||||
self.org:delete_process(self.id)
|
||||
end
|
||||
)
|
||||
end
|
||||
)
|
||||
end
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
--- (Required) Add to module table
|
||||
modpol.modules.send_token = send_token
|
@ -43,10 +43,13 @@ function module_template:initiate(result)
|
||||
-- call interaction functions here!
|
||||
|
||||
-- concluding functions:
|
||||
-- call these wherever process might end;
|
||||
-- first, where appropriate, return users to dashboards.
|
||||
-- second, result:
|
||||
-- may need to put result in self.data.result
|
||||
-- if process ends in another function
|
||||
-- call this when module is successful (not for abort):
|
||||
if result then result() end
|
||||
-- third, delete the process
|
||||
-- call this wherever process might end:
|
||||
self.org:delete_process(self.id)
|
||||
end
|
||||
|
||||
|
213
modpol_core/modules/tokenomics.lua
Normal file
213
modpol_core/modules/tokenomics.lua
Normal file
@ -0,0 +1,213 @@
|
||||
--- tokenomics
|
||||
-- @module tokenomics
|
||||
-- Depends on consent
|
||||
|
||||
local tokenomics = {
|
||||
name = "Tokenomics",
|
||||
slug = "tokenomics",
|
||||
desc = "A simple library for token economics",
|
||||
hide = true;
|
||||
}
|
||||
|
||||
--- (Required) Data for module
|
||||
-- Variables that module uses during the course of a process
|
||||
-- Can be blank
|
||||
tokenomics.data = {
|
||||
result = nil
|
||||
}
|
||||
|
||||
--- (Required): 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,
|
||||
token_slug = "token",
|
||||
token_variables = {
|
||||
treasury = 0,
|
||||
negative_spend = true,
|
||||
balances = {}
|
||||
}
|
||||
}
|
||||
|
||||
--- (Required): initiate function: creates a token in an org
|
||||
-- set up the token data structure
|
||||
-- create an org treasury
|
||||
-- @param result (optional) Callback if this module is embedded in other modules
|
||||
-- @function initiate
|
||||
function tokenomics:initiate(result)
|
||||
-- TODO need to create a series of interactions to get the info from users
|
||||
self.data.result = result
|
||||
if self.org.tokens and self.org.tokens[slug] then
|
||||
modpol.interactions.message(
|
||||
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
|
||||
end
|
||||
end
|
||||
|
||||
function tokenomics:create_token()
|
||||
if not self.org.tokens then self.org.tokens = {} end
|
||||
self.org.tokens[self.config.token_slug] =
|
||||
self.config.token_variables
|
||||
self.org:record("Created token "..self.config.token_slug,
|
||||
self.slug)
|
||||
modpol.interactions.message_org(
|
||||
self.initiator, self.org.id,
|
||||
"Created token "..self.config.token_slug)
|
||||
if self.data.result then self.data.result() end
|
||||
-- call this wherever process might end:
|
||||
self.org:delete_process(self.id)
|
||||
end
|
||||
|
||||
-----------------------------------------
|
||||
-- Utility functions
|
||||
-- all need to account for the fact that some users may not yet have balances
|
||||
-- all need to write to persistent data
|
||||
-- amount can be positive or negative (except transfer)
|
||||
|
||||
-- returns balance
|
||||
-- if no user, get treasury balance
|
||||
-- @param org Name (string) or id (num)
|
||||
-- @param token Slug (string)
|
||||
-- @param user Name (string)
|
||||
function tokenomics.balance(org, token, user)
|
||||
local this_org = modpol.orgs.get_org(org)
|
||||
if not this_org[token] then return nil, "Token not found" end
|
||||
if not user then
|
||||
return this_org[token].treasury
|
||||
end
|
||||
if not this_org[user] then
|
||||
return nil, "User not found"
|
||||
else
|
||||
local user_balance = this_org[token].balances[user]
|
||||
if user_balance then
|
||||
return user_balance
|
||||
else
|
||||
return 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- @param org Org name (string) or id (number)
|
||||
-- @param token Token slug (string)
|
||||
function tokenomics.change_balance(org, token, user, amount)
|
||||
local this_org = modpol.orgs.get_org(org)
|
||||
if not this_org then
|
||||
return nil, "Cannot change balance: Org not found"
|
||||
elseif not this_org.tokens then
|
||||
return nil, "Cannot change balance: no tokens found"
|
||||
elseif not this_org.tokens[token] then
|
||||
return nil, "Cannot change balance: token not found"
|
||||
elseif not this_org.members[user] then
|
||||
return nil, "Cannot change balance: user not in org"
|
||||
elseif not tonumber(amount) then
|
||||
return nil, "Cannot change balance: invalid amount"
|
||||
else
|
||||
local old_balance = this_org.tokens[token].balances[user]
|
||||
if not old_balance then old_balance = 0 end
|
||||
local new_balance = old_balance + amount
|
||||
this_org.tokens[token].balances[user] = new_balance
|
||||
local msg = "Your balance of token "..token..
|
||||
" changed from "..old_balance.." to "..new_balance
|
||||
modpol.interactions.message(user, msg)
|
||||
self.org:record(
|
||||
"Changed token balance for "..user,self.slug)
|
||||
end
|
||||
end
|
||||
|
||||
-- @param amount Positive number
|
||||
function tokenomics.transfer(org, token, sender, recipient, amount)
|
||||
local sender_balance = tokenomics.balance(org, token, sender)
|
||||
local recipient_balance = tokenomics.balance(org, token, recipient)
|
||||
if not sender_balance or recipient_balance then
|
||||
return nil, "Transfer failed, user not found"
|
||||
else
|
||||
sender_balance = sender_balance - amount
|
||||
recipient_balance = recipient_balance + amount
|
||||
local neg_spend = modpol.orgs.get_org(org).tokens[token].negative_spend
|
||||
if sender_balance < 0 and not neg_spend then
|
||||
return nil, "Transfer failed, negative spend not allowed"
|
||||
else
|
||||
tokenomics.change_balance(
|
||||
org, token, sender, sender_balance)
|
||||
tokenomics.change_balance(
|
||||
org, token, recipient, recipient_balance)
|
||||
return "Transfer complete"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- @param amount Can be positive or negative, assumes flow from treasury to recipient
|
||||
function tokenomics.treasury_transfer(org, token, recipient, amount)
|
||||
local this_org = modpol.orgs.get_org(org)
|
||||
if not this_org then
|
||||
return nil, "Cannot transfer treasury: Org not found"
|
||||
elseif not this_org.tokens then
|
||||
return nil, "Cannot transfer treasury: no tokens found"
|
||||
elseif not this_org.tokens[token] then
|
||||
return nil, "Cannot transfer treasury: token not found"
|
||||
elseif not this_org.members[user] then
|
||||
return nil, "Cannot transfer treasury: user not in org"
|
||||
elseif not tonumber(amount) then
|
||||
return nil, "Cannot change balance: invalid amount"
|
||||
else
|
||||
local new_treasury = this_org.tokens[token].treasury - amount
|
||||
local new_recipient_balance = tokenomics.balance(org, token, recipient) + amount
|
||||
if new_treasury < 0 and not this_org.tokens[token].negative_spend then
|
||||
return nil, "Transfer failed, negative spend not allowed"
|
||||
elseif new_recipient_balance < 0 and not this_org.tokens[token].negative_spend then
|
||||
return nil, "Transfer failed, negative spend not allowed"
|
||||
else
|
||||
this_org.tokens[token].treasury = new_treasury
|
||||
self.org:record("Treasury payment",self.slug)
|
||||
tokenomics.change_balance(org, token, recipient, amount)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- creates new tokens in the org treasury
|
||||
function tokenomics.issue(org, token, amount)
|
||||
local this_org = modpol.orgs.get_org(org)
|
||||
if not this_org then
|
||||
return nil, "Cannot transfer treasury: Org not found"
|
||||
elseif not this_org.tokens then
|
||||
return nil, "Cannot transfer treasury: no tokens found"
|
||||
elseif not this_org.tokens[token] then
|
||||
return nil, "Cannot transfer treasury: token not found"
|
||||
elseif not tonumber(amount) then
|
||||
return nil, "Cannot change balance: invalid amount"
|
||||
else
|
||||
this_org.tokens[token].treasury =
|
||||
this_org.tokens[token].treasury + amount
|
||||
self.org:record("Issued tokes to treasury","tokenomics")
|
||||
end
|
||||
end
|
||||
|
||||
------------------------------------------
|
||||
|
||||
--- (Required) Add to module table
|
||||
modpol.modules.tokenomics = tokenomics
|
@ -76,6 +76,8 @@ end
|
||||
--- Deletes all orgs except for the
|
||||
-- @function modpol.orgs.reset
|
||||
function modpol.orgs.reset()
|
||||
local instance_members =
|
||||
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"
|
||||
@ -84,9 +86,9 @@ function modpol.orgs.reset()
|
||||
|
||||
modpol.orgs.array[1] = nil
|
||||
modpol.instance = modpol.orgs.init_instance()
|
||||
|
||||
modpol.instance.members = instance_members
|
||||
|
||||
modpol.ocutil.log('Reset all orgs')
|
||||
modpol.ocutil.log('All orgs reset')
|
||||
modpol.orgs:record('Resetting all orgs', 'org_reset')
|
||||
end
|
||||
|
||||
|
@ -7,33 +7,18 @@
|
||||
-- @param intiator Initiator for module
|
||||
-- @param config Config for module
|
||||
-- @param result
|
||||
function modpol.orgs:call_module(module_slug, initiator, config, 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
|
||||
|
||||
local empty_index = nil
|
||||
-- linear search for empty process slots (lazy deletion)
|
||||
for k, v in ipairs(self.processes) do
|
||||
if v == 'deleted' then
|
||||
empty_index = k
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
local index
|
||||
-- attempts to fill empty spots in list, otherwise appends to end
|
||||
if empty_index then
|
||||
index = empty_index
|
||||
else
|
||||
index = #self.processes + 1
|
||||
end
|
||||
local index = #self.processes + 1
|
||||
|
||||
local module = modpol.modules[module_slug]
|
||||
|
||||
-- sets default values for undeclared config variables
|
||||
if #module.config > 0 then
|
||||
if modpol.util.num_pairs(module.config) > 0 and config then
|
||||
for k, v in pairs(module.config) do
|
||||
if config[k] == nil then
|
||||
config[k] = v
|
||||
@ -47,24 +32,61 @@ function modpol.orgs:call_module(module_slug, initiator, config, result)
|
||||
initiator = initiator,
|
||||
org = self,
|
||||
id = index,
|
||||
parent_id = parent_id,
|
||||
children = {},
|
||||
config = config,
|
||||
data = module.data,
|
||||
data = modpol.util.copy_table(module.data),
|
||||
slug = module_slug
|
||||
}
|
||||
|
||||
-- call module wrapper for modules, passes its id to child process when called
|
||||
function new_process:call_module(module_slug, initiator, config, result)
|
||||
local child_id = self.org:call_module(module_slug, initiator, config, result, self.id)
|
||||
table.insert(self.children, child_id)
|
||||
end
|
||||
|
||||
setmetatable(new_process, new_process.metatable)
|
||||
|
||||
self.processes[index] = new_process
|
||||
self.processes[index]:initiate(result)
|
||||
local msg = "Initiating "..module_slug..
|
||||
" process id "..index.." in org "..self.name
|
||||
|
||||
return index
|
||||
end
|
||||
|
||||
--- Delete process by id
|
||||
-- @function modpol.orgs:delete_process
|
||||
-- @param id Id of process
|
||||
function modpol.orgs:delete_process(id)
|
||||
self.processes[id] = 'deleted'
|
||||
|
||||
function modpol.orgs:get_root_process(id)
|
||||
local process = self.processes[id]
|
||||
while (process.parent_id) do
|
||||
process = self.processes[process.parent_id]
|
||||
end
|
||||
return process
|
||||
end
|
||||
|
||||
function modpol.orgs:delete_process(id)
|
||||
local process = self.processes[id]
|
||||
if process and process ~= "deleted" then
|
||||
-- recursively deletes any children
|
||||
if #process.children > 0 then
|
||||
for i, child_id in pairs(process.children) do
|
||||
self:delete_process(child_id)
|
||||
end
|
||||
end
|
||||
local msg = "Deleting " .. self.processes[id].slug .. " process id "..id.." in org "..self.name
|
||||
modpol.ocutil.log(msg)
|
||||
self:record(msg, self.processes[id].slug)
|
||||
self:wipe_pending_actions(id)
|
||||
-- sets process to 'deleted' in process table
|
||||
self.processes[id] = 'deleted'
|
||||
end
|
||||
end
|
||||
|
||||
--- Delete process tree by id
|
||||
-- @function modpol.orgs:delete_process_tree
|
||||
-- @param id Id of process tree
|
||||
function modpol.orgs:delete_process_tree(id)
|
||||
self:delete_process(self:get_root_process(id).id)
|
||||
end
|
||||
|
||||
--- Add a new pending action
|
||||
@ -124,8 +146,15 @@ function modpol.orgs:interact(process_id, user)
|
||||
local process = self.processes[process_id]
|
||||
if self.pending[user] then
|
||||
local callback = self.pending[user][process_id]
|
||||
if callback then
|
||||
if callback and process ~= "deleted" then
|
||||
-- get data in case callback ends process
|
||||
local slug = self.processes[process_id].slug
|
||||
-- run callback
|
||||
process[callback](process, user)
|
||||
-- record org data
|
||||
local msg = "Updating "..slug..
|
||||
" process id "..process_id.." in org "..self.name
|
||||
self:record(msg, slug)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
27
modpol_core/orgs/users.lua
Normal file
27
modpol_core/orgs/users.lua
Normal file
@ -0,0 +1,27 @@
|
||||
-- /users.lua
|
||||
-- User-related functions for Modular Politics
|
||||
|
||||
-- ===================================================================
|
||||
-- Function: modpol.list_users
|
||||
-- Params: org
|
||||
-- Outputs: Table of user names
|
||||
--
|
||||
-- This may be overwritten by the platform-specific interface
|
||||
|
||||
modpol.list_users = function(org)
|
||||
local users = {}
|
||||
if (org == nil) then -- no specified org; all players
|
||||
if modpol.orgs["instance"]
|
||||
and modpol.orgs["instance"]["members"] then
|
||||
-- if instance exists and has membership
|
||||
users = modpol.orgs["instance"]["members"]
|
||||
else
|
||||
users = {}
|
||||
end
|
||||
else -- if an org is specified
|
||||
if (modpol.orgs[org] ~= nil) then -- org exists
|
||||
users = modpol.orgs[org]["members"]
|
||||
end
|
||||
end
|
||||
return users
|
||||
end
|
@ -9,8 +9,14 @@ modpol.util = {}
|
||||
-- @return copy of table
|
||||
function modpol.util.copy_table(t)
|
||||
local t2 = {}
|
||||
for k,v in pairs(t) do
|
||||
t2[k] = v
|
||||
if pairs(t) then
|
||||
for k,v in pairs(t) do
|
||||
if type(v) == "table" then
|
||||
t2[k] = modpol.util.copy_table(v)
|
||||
else
|
||||
t2[k] = v
|
||||
end
|
||||
end
|
||||
end
|
||||
return t2
|
||||
end
|
||||
|
Reference in New Issue
Block a user