interactions.lua 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368
  1. -- INTERACTIONS.LUA (CLI)
  2. -- User interaction functions for Modular Politics
  3. -- Called by modpol.lua
  4. modpol.interactions = {}
  5. -- DASHBOARDS
  6. -- ==========
  7. -- Function: modpol.interactions.dashboard(user)
  8. -- Params: user (string)
  9. -- Q: Should this return a menu of commands relevant to the specific user?
  10. -- Output: Displays a menu of commands to the user
  11. -- TKTK currently just prints all of modpol---needs major improvement
  12. function modpol.interactions.dashboard(user)
  13. -- adds user to root org if not already in it
  14. if not modpol.instance:has_member(user) then
  15. modpol.instance:add_member(user)
  16. end
  17. local all_users = modpol.instance:list_members()
  18. print('\n-=< MODPOL DASHBOARD >=-')
  19. print('All orgs: (user orgs indicated by *)')
  20. for id, org in ipairs(modpol.orgs.array) do
  21. if type(org) == "table" then
  22. local indicator = ""
  23. if org:has_member(user) then indicator = "*" end
  24. print('['..id..'] '..indicator..org.name)
  25. end
  26. end
  27. -- pending list
  28. local user_pending = {}
  29. local user_pending_count = 0
  30. for k,v in ipairs(modpol.orgs.array) do
  31. if v.pending and v.pending[user] then
  32. if modpol.util.num_pairs(v.pending[user]) ~= 0 then
  33. table.insert(user_pending, v.name)
  34. user_pending_count = user_pending_count + 1
  35. end
  36. end
  37. end
  38. print('Pending actions in: '..table.concat(user_pending,', '))
  39. print('All users: ' .. table.concat(all_users, ', '))
  40. print()
  41. print("Commands: (O)rg, (U)ser, Enter to close")
  42. local sel = io.read()
  43. if sel == "O" or sel == "o" then
  44. print('Access which org id?')
  45. sel = io.read()
  46. print()
  47. if modpol.orgs.array[tonumber(sel)] then
  48. local sel_org = modpol.orgs.array[tonumber(sel)].name
  49. modpol.interactions.org_dashboard(user, sel_org)
  50. else
  51. print("Org id not found")
  52. modpol.interactions.dashboard(user)
  53. end
  54. elseif sel == "U" or sel == "u" then
  55. print("Access which user?")
  56. sel = io.read()
  57. print()
  58. if modpol.instance:has_member(sel) then
  59. modpol.interactions.user_dashboard(
  60. user, sel,
  61. function()
  62. modpol.interactions.dashboard(user)
  63. end
  64. )
  65. else
  66. print("User name not found")
  67. modpol.interactions.dashboard(user)
  68. end
  69. end
  70. end
  71. -- Function: modpol.interactions.org_dashboard
  72. -- Params: user (string), org_string (string or id)
  73. -- Output: Displays a menu of org-specific commands to the user
  74. function modpol.interactions.org_dashboard(user, org_string)
  75. local org = modpol.orgs.get_org(org_string)
  76. if not org then return nil end
  77. -- identify parent
  78. local parent = ""
  79. if org.id == 1 then
  80. parent = "none"
  81. else
  82. parent = modpol.orgs.get_org(org.parent).name
  83. end
  84. -- identify children
  85. local children = {}
  86. for k,v in ipairs(org.children) do
  87. local this_child = modpol.orgs.get_org(v)
  88. table.insert(children, this_child.name)
  89. end
  90. -- prepare modules menu
  91. local modules = {}
  92. if org.modules then
  93. for k,v in pairs(org.modules) do
  94. if not v.hide then -- hide utility modules
  95. local module_entry = v.slug
  96. table.insert(modules, module_entry)
  97. end
  98. end
  99. end
  100. table.sort(modules)
  101. -- list pending
  102. local process_msg = #org.processes .. " total processes"
  103. if org.pending[user] then
  104. process_msg = process_msg .. " (" ..
  105. modpol.util.num_pairs(org.pending[user]) .. " pending)"
  106. else
  107. process_msg = process_msg .. " (0 pending)"
  108. end
  109. -- set up output
  110. print('\n-=< ORG DASHBOARD >=-')
  111. print("Org: " .. org.name)
  112. print("Parent: " .. parent)
  113. print("Members: " .. table.concat(org.members, ", "))
  114. print("Child orgs: " .. table.concat(children, ", "))
  115. print("Modules: " .. table.concat(modules, ", "))
  116. print("Pending: " .. process_msg)
  117. print()
  118. print("Commands: (M)odules, (P)ending, (B)ack")
  119. local sel = io.read()
  120. print()
  121. if sel == 'm' or sel == 'M' then
  122. print("Type module name: ")
  123. local module_sel = io.read()
  124. print()
  125. local module_result = false
  126. for k,v in ipairs(modules) do
  127. if v == module_sel then
  128. module_result = true
  129. end
  130. end
  131. if module_result then
  132. org:call_module(module_sel, user)
  133. else
  134. print("Error: Module not found.")
  135. end
  136. elseif sel == 'p' or sel == 'P' then
  137. local processes = {}
  138. print("All processes: (* indicates pending)")
  139. for i,v in ipairs(org.processes) do
  140. local active = ''
  141. if org.pending[user] then
  142. if org.pending[user][v.id] then
  143. active = '*'
  144. end
  145. end
  146. print("["..v.id.."] "..v.slug..active)
  147. end
  148. print()
  149. print("Interact with which one (use [id] number)?")
  150. local to_interact = io.read()
  151. local process = org.processes[tonumber(to_interact)]
  152. if not process then return end
  153. if org:has_pending_actions(user) then
  154. if org.pending[user][process.id] then
  155. org:interact(process.id, user)
  156. end
  157. end
  158. elseif sel == 'b' or sel == 'B' then
  159. modpol.interactions.dashboard(user)
  160. else
  161. print("Command not found")
  162. modpol.interactions.org_dashboard(user, org.name)
  163. end
  164. end
  165. --- Function: modpol.interactions.user_dashboard
  166. -- Displays a dashboard about a particular user
  167. -- @param viewer Name of user viewing the dashboard (string)
  168. -- @param user Name of user being viewed (string)
  169. -- @param completion Optional function to call on Done button
  170. function modpol.interactions.user_dashboard(viewer, user, completion)
  171. local user_orgs = {}
  172. local user_modules = {}
  173. print("\n-=< USER DASHBOARD: "..user.." >=-")
  174. print("User's orgs:")
  175. for id, org in ipairs(modpol.orgs.array) do
  176. if type(org) == "table" then
  177. if org:has_member(user) then
  178. print(org.name)
  179. end
  180. end
  181. end
  182. print()
  183. print("Commands: (M)essage user, Enter when done")
  184. local sel = io.read()
  185. if sel == "M" or sel == "m" then
  186. modpol.interactions.message_user(
  187. viewer, user)
  188. completion()
  189. else
  190. completion()
  191. end
  192. end
  193. -- buttons: message, done
  194. -- INTERACTION PRIMITIVES
  195. -- ======================
  196. -- Function: modpol.interactions.message
  197. -- Produces a brief message to a user
  198. -- input: user (string), message (string)
  199. -- output: prints message to CLI
  200. function modpol.interactions.message(user, message)
  201. print(user .. ": " .. message)
  202. end
  203. --- Function: modpol.interactions.message_user
  204. -- Gets and sends a message from one user to another
  205. -- @param sender Name of user sending (string)
  206. -- @param recipient Name of user receiving (string)
  207. function modpol.interactions.message_user(sender, recipient)
  208. print("Enter your message for "..recipient..":")
  209. local sel = io.read()
  210. modpol.interactions.message(
  211. recipient,
  212. sel.." [from "..sender.."]")
  213. end
  214. --- Function: modpol.interactions.display
  215. -- Displays complex data to a user
  216. -- @param user Name of target user (string)
  217. -- @param message Content of message (string or table of strings)
  218. -- @param done Optional function for what happens when user is done
  219. function modpol.interactions.display(user, message, completion)
  220. local output = ""
  221. if type(message) == table then
  222. output = table.concat(message,"\n")
  223. elseif type(message) == string then
  224. output = message
  225. elseif type(message) == number then
  226. output = message
  227. else
  228. return nil, "Error: message not typed for display"
  229. end
  230. print(message)
  231. print("\nEnter to continue")
  232. io.read()
  233. if completion then completion() else
  234. modpol.intereactions.dashboard(user)
  235. end
  236. end
  237. -- Function: modpol.interactions.text_query
  238. -- input: User (string), Query (string), func (function)
  239. -- func input: user input (string)
  240. -- output: Applies "func" to user input
  241. function modpol.interactions.text_query(user, query, func)
  242. print(user .. ": " .. query)
  243. answer = io.read()
  244. func(answer)
  245. end
  246. -- Function: dropdown_query
  247. -- input: user (string), label (string), options (table of strings), func(choice) (function)
  248. -- func input: choice (string)
  249. -- output: calls func on choice
  250. function modpol.interactions.dropdown_query(user, label, options, func)
  251. -- set up options
  252. local options_display = ""
  253. local options_number = 0
  254. for k,v in ipairs(options) do
  255. options_display = options_display .. k .. ". " ..
  256. options[k] .. "\n"
  257. options_number = options_number + 1
  258. end
  259. options_display = options_display .. "Select number:"
  260. if options_number == 0 then
  261. print("Error: No options given for dropdown")
  262. return nil
  263. end
  264. -- begin displaying
  265. print(user .. ": " .. label)
  266. print(options_display)
  267. -- read input and produce output
  268. local answer
  269. answer = io.read()
  270. answer = tonumber(answer)
  271. if answer then
  272. if answer >= 1 and answer <= options_number then
  273. print("Selection: " .. options[answer])
  274. func(options[answer])
  275. else
  276. print("Error: Not in dropdown range")
  277. return nil
  278. end
  279. else
  280. print("Error: Must be a number")
  281. return nil
  282. end
  283. end
  284. -- Function: modpol.binary_poll_user(user, question)
  285. -- Params: user (string), question (string), func (function)
  286. -- func input: user input (string: y/n)
  287. -- Output: Applies "func" to user input
  288. function modpol.interactions.binary_poll_user(user, question, func)
  289. local query = "Poll for " .. user .. " (y/n): ".. question
  290. local answer
  291. repeat
  292. print(query)
  293. answer = io.read()
  294. until answer == "y" or answer == "n"
  295. if answer == "y" then
  296. modpol.interactions.message(user, "Response recorded")
  297. func("Yes")
  298. elseif answer == "n" then
  299. modpol.interactions.message(user, "Response recorded")
  300. func("No")
  301. else
  302. modpol.interactions.message(user, "Error: invalid response")
  303. end
  304. end
  305. -- COMPLEX INTERACTIONS
  306. -- ====================
  307. -- Function: modpol.interactions.message_org
  308. -- input: initiator (string), org (number or string), message (string)
  309. -- output: broadcasts message to all org members
  310. function modpol.interactions.message_org(initiator, org, message)
  311. local this_org = modpol.orgs.get_org(org)
  312. local users = this_org:list_members()
  313. for k,v in ipairs(users) do
  314. modpol.interactions.message(v, message)
  315. end
  316. end
  317. -- Function: modpol.interactions.binary_poll_org
  318. -- input: initator (user string), org_id (number)
  319. -- output: gets question from initiator, asks all org members, broadcasts answers
  320. -- TESTING
  321. --testing command
  322. function modpol.msg(text)
  323. modpol.interactions.message("TEST MSG: ",text)
  324. end