interactions.lua 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. --- INTERACTIONS.LUA (CLI).
  2. -- User interaction functions for Modular Politics
  3. -- Called by modpol.lua
  4. -- @module modpol.interactions
  5. modpol.interactions = {}
  6. -- DASHBOARDS
  7. -- ==========
  8. --- Output: Display a menu of commands to the user
  9. -- @function modpol.interactions.dashboard
  10. -- @param user (string)
  11. -- Q: Should this return a menu of commands relevant to the specific user?
  12. -- TKTK currently just prints all of modpol---needs major improvement
  13. function modpol.interactions.dashboard(user)
  14. -- adds user to root org if not already in it
  15. if not modpol.instance:has_member(user) then
  16. modpol.instance:add_member(user)
  17. end
  18. local all_users = modpol.instance:list_members()
  19. print('\n-=< MODPOL DASHBOARD >=-')
  20. print('All orgs: (user orgs indicated by *)')
  21. for id, org in ipairs(modpol.orgs.array) do
  22. if type(org) == "table" then
  23. local indicator = ""
  24. if org:has_member(user) then indicator = "*" end
  25. print('['..id..'] '..indicator..org.name)
  26. end
  27. end
  28. -- pending list
  29. local user_pending = {}
  30. local user_pending_count = 0
  31. for k,v in ipairs(modpol.orgs.array) do
  32. if v.pending and v.pending[user] then
  33. table.insert(user_pending, v.name)
  34. user_pending_count = user_pending_count + 1
  35. end
  36. end
  37. print('Pending actions in: '..table.concat(user_pending,', '))
  38. print('All users: ' .. table.concat(all_users, ', '))
  39. print()
  40. print('Access which org id?')
  41. local sel = io.read()
  42. print()
  43. if modpol.orgs.array[tonumber(sel)] then
  44. local sel_org = modpol.orgs.array[tonumber(sel)].name
  45. modpol.interactions.org_dashboard(user, sel_org)
  46. else
  47. print("Org id not found.")
  48. end
  49. end
  50. --- Output: Displays a menu of org-specific commands to the user
  51. -- @function modpol.interactions.org_dashboard
  52. -- @param user (string)
  53. -- @param org_string (string or id)
  54. function modpol.interactions.org_dashboard(user, org_string)
  55. local org = modpol.orgs.get_org(org_string)
  56. if not org then return nil end
  57. -- identify parent
  58. local parent = ""
  59. if org.id == 1 then
  60. parent = "none"
  61. else
  62. parent = modpol.orgs.get_org(org.parent).name
  63. end
  64. -- identify children
  65. local children = {}
  66. for k,v in ipairs(org.children) do
  67. local this_child = modpol.orgs.get_org(v)
  68. table.insert(children, this_child.name)
  69. end
  70. -- prepare modules menu
  71. local modules = {}
  72. if org.modules then
  73. for k,v in pairs(org.modules) do
  74. if not v.hide then -- hide utility modules
  75. local module_entry = v.slug
  76. table.insert(modules, module_entry)
  77. end
  78. end
  79. end
  80. table.sort(modules)
  81. -- list pending
  82. local process_msg = #org.processes .. " total processes"
  83. if org.pending[user] then
  84. process_msg = process_msg .. " (" ..
  85. modpol.util.num_pairs(org.pending[user]) .. " pending)"
  86. else
  87. process_msg = process_msg .. " (0 pending)"
  88. end
  89. -- set up output
  90. print('\n-=< ORG DASHBOARD >=-')
  91. print("Org: " .. org.name)
  92. print("Parent: " .. parent)
  93. print("Members: " .. table.concat(org.members, ", "))
  94. print("Children: " .. table.concat(children, ", "))
  95. print("Modules: " .. table.concat(modules, ", "))
  96. print("Pending: " .. process_msg)
  97. print()
  98. print("Commands: (M)odules, (P)ending, (B)ack")
  99. local sel = io.read()
  100. print()
  101. if sel == 'm' or sel == 'M' then
  102. print("Type module name: ")
  103. local module_sel = io.read()
  104. print()
  105. local module_result = false
  106. for k,v in ipairs(modules) do
  107. if v == module_sel then
  108. module_result = true
  109. end
  110. end
  111. if module_result then
  112. org:call_module(module_sel, user)
  113. else
  114. print("Error: Module not found.")
  115. end
  116. elseif sel == 'p' or sel == 'P' then
  117. local processes = {}
  118. print("All processes: (* indicates pending)")
  119. for i,v in ipairs(org.processes) do
  120. local active = ''
  121. if org.pending[user] then
  122. if org.pending[user][v.id] then
  123. active = '*'
  124. end
  125. end
  126. print("["..v.id.."] "..v.slug..active)
  127. end
  128. print()
  129. print("Interact with which one (use [id] number)?")
  130. local to_interact = io.read()
  131. local process = org.processes[tonumber(to_interact)]
  132. if not process then return end
  133. if org:has_pending_actions(user) then
  134. if org.pending[user][process.id] then
  135. org:interact(process.id, user)
  136. end
  137. end
  138. elseif sel == 'b' or sel == 'B' then
  139. modpol.interactions.dashboard(user)
  140. else
  141. print("Command not found")
  142. modpol.interactions.org_dashboard(user, org.name)
  143. end
  144. end
  145. -- Function: modpol.interactions.policy_dashboard
  146. -- input: user (string), org_id (int), policy (string)
  147. -- if policy is nil, enables creating a new policy
  148. -- output: opens a dashboard for viewing/editing policy details
  149. -- TODO
  150. --- Output: Prints message to CLI
  151. -- @function modpol.interactions.message
  152. -- @param user (string)
  153. -- @param message (string)
  154. function modpol.interactions.message(user, message)
  155. print(user .. ": " .. message)
  156. end
  157. --- Output: Applies "func" to user input.
  158. -- Func input: user input (string)
  159. -- @function modpol.interactions.text_query
  160. -- @param user (string)
  161. -- @param query (string)
  162. -- @param func (function)
  163. function modpol.interactions.text_query(user, query, func)
  164. print(user .. ": " .. query)
  165. answer = io.read()
  166. func(answer)
  167. end
  168. --- Output: Calls func on choice.
  169. -- func input: choice (string)
  170. -- @function modpol.interactions.dropdown_query
  171. -- @param user (string)
  172. -- @param label (string)
  173. -- @param options (table of strings)
  174. -- @param func (choice) (function)
  175. function modpol.interactions.dropdown_query(user, label, options, func)
  176. -- set up options
  177. local options_display = ""
  178. local options_number = 0
  179. for k,v in ipairs(options) do
  180. options_display = options_display .. k .. ". " ..
  181. options[k] .. "\n"
  182. options_number = options_number + 1
  183. end
  184. options_display = options_display .. "Select number:"
  185. if options_number == 0 then
  186. print("Error: No options given for dropdown")
  187. return nil
  188. end
  189. -- begin displaying
  190. print(user .. ": " .. label)
  191. print(options_display)
  192. -- read input and produce output
  193. local answer
  194. answer = io.read()
  195. answer = tonumber(answer)
  196. if answer then
  197. if answer >= 1 and answer <= options_number then
  198. print("Selection: " .. options[answer])
  199. func(options[answer])
  200. else
  201. print("Error: Not in dropdown range")
  202. return nil
  203. end
  204. else
  205. print("Error: Must be a number")
  206. return nil
  207. end
  208. end
  209. --- Output: Applies "func" to user input.
  210. -- Func input: user input (string: y/n)
  211. -- @function modpol.binary_poll_user(user, question)
  212. -- @param user (string)
  213. -- @param question (string)
  214. -- @param func (function)
  215. function modpol.interactions.binary_poll_user(user, question, func)
  216. local query = "Poll for " .. user .. " (y/n): ".. question
  217. local answer
  218. repeat
  219. print(query)
  220. answer = io.read()
  221. until answer == "y" or answer == "n"
  222. if answer == "y" then
  223. modpol.interactions.message(user, "Response recorded")
  224. func("Yes")
  225. elseif answer == "n" then
  226. modpol.interactions.message(user, "Response recorded")
  227. func("No")
  228. else
  229. modpol.interactions.message(user, "Error: invalid response")
  230. end
  231. end
  232. -- COMPLEX INTERACTIONS
  233. -- ====================
  234. --- Output: broadcasts message to all org members
  235. -- @function modpol.interactions.message_org
  236. -- @param initiator (string)
  237. -- @param org (number or string)
  238. -- @param message (string)
  239. function modpol.interactions.message_org(initiator, org, message)
  240. local this_org = modpol.orgs.get_org(org)
  241. local users = this_org:list_members()
  242. for k,v in ipairs(users) do
  243. modpol.interactions.message(v, message)
  244. end
  245. end
  246. -- Function: modpol.interactions.binary_poll_org
  247. -- input: initator (user string), org_id (number)
  248. -- output: gets question from initiator, asks all org members, broadcasts answers