ask.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516
  1. package server
  2. /* This file contains dialog helpers for the client. */
  3. // import "gitlab.com/beoran/woe/monolog"
  4. import t "gitlab.com/beoran/woe/telnet"
  5. import "gitlab.com/beoran/woe/telnet"
  6. import "gitlab.com/beoran/woe/world"
  7. // import "gitlab.com/beoran/woe/monolog"
  8. import "bytes"
  9. import "strings"
  10. import "regexp"
  11. // import "fmt"
  12. import "strconv"
  13. // import "strings"
  14. // Switches to "password" mode.
  15. func (me *Client) PasswordMode() telnet.Event {
  16. // The server sends "IAC WILL ECHO", meaning "I, the server, will do any
  17. // echoing from now on." The client should acknowledge this with an IAC DO
  18. // ECHO, and then stop putting echoed text in the input buffer.
  19. // It should also do whatever is appropriate for password entry to the input
  20. // box thing - for example, it might * it out. Text entered in server-echoes
  21. // mode should also not be placed any command history.
  22. // don't use the Q state machne for echos
  23. me.telnet.TelnetSendBytes(t.TELNET_IAC, t.TELNET_WILL, t.TELNET_TELOPT_ECHO)
  24. tev, _, _ := me.TryReadEvent(100)
  25. if tev != nil && !telnet.IsEventType(tev, t.TELNET_DO_EVENT) {
  26. return tev
  27. }
  28. return nil
  29. }
  30. // Switches to "normal, or non-password mode.
  31. func (me *Client) NormalMode() telnet.Event {
  32. // When the server wants the client to start local echoing again, it s}s
  33. // "IAC WONT ECHO" - the client must respond to this with "IAC DONT ECHO".
  34. // Again don't use Q state machine.
  35. me.telnet.TelnetSendBytes(t.TELNET_IAC, t.TELNET_WONT, t.TELNET_TELOPT_ECHO)
  36. tev, _, _ := me.TryReadEvent(100)
  37. if tev != nil && !telnet.IsEventType(tev, t.TELNET_DONT_EVENT) {
  38. return tev
  39. }
  40. return nil
  41. }
  42. func (me *Client) Printf(format string, args ...interface{}) {
  43. me.telnet.TelnetPrintf(format, args...)
  44. }
  45. func (me *Client) ColorTest() {
  46. me.Printf("\033[1mBold\033[0m\r\n")
  47. me.Printf("\033[3mItalic\033[0m\r\n")
  48. me.Printf("\033[4mUnderline\033[0m\r\n")
  49. for fg := 30; fg < 38; fg++ {
  50. me.Printf("\033[%dmForeground Color %d\033[0m\r\n", fg, fg)
  51. me.Printf("\033[1;%dmBold Foreground Color %d\033[0m\r\n", fg, fg)
  52. }
  53. for bg := 40; bg < 48; bg++ {
  54. me.Printf("\033[%dmBackground Color %d\033[0m\r\n", bg, bg)
  55. me.Printf("\033[1;%dmBold Background Color %d\033[0m\r\n", bg, bg)
  56. }
  57. }
  58. // Blockingly reads a single command from the client
  59. func (me *Client) ReadCommand() (something []byte) {
  60. something = nil
  61. for something == nil {
  62. something, _, _ = me.TryRead(-1)
  63. if something != nil {
  64. something = bytes.TrimRight(something, "\r\n")
  65. return something
  66. }
  67. }
  68. return nil
  69. }
  70. func (me *Client) AskSomething(prompt string, re string, nomatch_prompt string, noecho bool) (something []byte) {
  71. something = nil
  72. if noecho {
  73. me.PasswordMode()
  74. }
  75. for something == nil || len(something) == 0 {
  76. me.Printf("%s", prompt)
  77. something, _, _ = me.TryRead(-1)
  78. if something != nil {
  79. something = bytes.TrimRight(something, "\r\n")
  80. if len(re) > 0 {
  81. ok, _ := regexp.Match(re, something)
  82. if !ok {
  83. me.Printf("\n%s\n", nomatch_prompt)
  84. something = nil
  85. }
  86. }
  87. }
  88. }
  89. if noecho {
  90. me.NormalMode()
  91. me.Printf("\n")
  92. }
  93. return something
  94. }
  95. func (me *Client) AskYesNo(prompt string) bool {
  96. res := me.AskSomething(prompt+" (y/n)", "[ynYN]", "Please answer y or n.", false)
  97. if res[0] == 'Y' || res[0] == 'y' {
  98. return true
  99. } else {
  100. return false
  101. }
  102. }
  103. // Interface for an item in a list of options.
  104. type AskOption interface {
  105. // Name of the option, also used to compare input
  106. AskName() string
  107. // Short description of the option, shown after name
  108. AskShort() string
  109. // Long description, displayed if "help name" is requested
  110. AskLong() string
  111. // Accound privilege required or the option to be selectable.
  112. AskPrivilege() world.Privilege
  113. }
  114. type AskOptionList interface {
  115. AskOptionListLen() int
  116. AskOptionListGet(index int) AskOption
  117. }
  118. type TrivialAskOption string
  119. func (me TrivialAskOption) AskName() string {
  120. return string(me)
  121. }
  122. func (me TrivialAskOption) AskLong() string {
  123. return ""
  124. }
  125. func (me TrivialAskOption) AskShort() string {
  126. return ""
  127. }
  128. func (me TrivialAskOption) AskPrivilege() world.Privilege {
  129. return world.PRIVILEGE_ZERO
  130. }
  131. type TrivialAskOptionList []TrivialAskOption
  132. func (me TrivialAskOptionList) AskOptionListLen() int {
  133. return len(me)
  134. }
  135. func (me TrivialAskOptionList) AskOptionListGet(index int) AskOption {
  136. return me[index]
  137. }
  138. type SimpleAskOption struct {
  139. Name string
  140. Short string
  141. Long string
  142. Privilege world.Privilege
  143. }
  144. func (me SimpleAskOption) AskName() string {
  145. return me.Name
  146. }
  147. func (me SimpleAskOption) AskShort() string {
  148. return me.Short
  149. }
  150. func (me SimpleAskOption) AskLong() string {
  151. return me.Long
  152. }
  153. func (me SimpleAskOption) AskPrivilege() world.Privilege {
  154. return me.Privilege
  155. }
  156. type SimpleAskOptionList []SimpleAskOption
  157. func (me SimpleAskOptionList) AskOptionListLen() int {
  158. return len(me)
  159. }
  160. func (me SimpleAskOptionList) AskOptionListGet(index int) AskOption {
  161. return me[index]
  162. }
  163. type JobListAsker []world.Job
  164. func (me JobListAsker) AskOptionListLen() int {
  165. return len(me)
  166. }
  167. func (me JobListAsker) AskOptionListGet(index int) AskOption {
  168. return me[index]
  169. }
  170. type KinListAsker []world.Kin
  171. func (me KinListAsker) AskOptionListLen() int {
  172. return len(me)
  173. }
  174. func (me KinListAsker) AskOptionListGet(index int) AskOption {
  175. return me[index]
  176. }
  177. type GenderListAsker []world.Gender
  178. func (me GenderListAsker) AskOptionListLen() int {
  179. return len(me)
  180. }
  181. func (me GenderListAsker) AskOptionListGet(index int) AskOption {
  182. return me[index]
  183. }
  184. type AskOptionSlice []AskOption
  185. type AskOptionFilterFunc func(AskOption) AskOption
  186. func (me AskOptionSlice) AskOptionListLen() int {
  187. return len(me)
  188. }
  189. func (me AskOptionSlice) AskOptionListGet(index int) AskOption {
  190. return me[index]
  191. }
  192. func AskOptionListEach(me AskOptionList, cb AskOptionFilterFunc) AskOption {
  193. for i := 0; i < me.AskOptionListLen(); i++ {
  194. res := cb(me.AskOptionListGet(i))
  195. if res != nil {
  196. return res
  197. }
  198. }
  199. return nil
  200. }
  201. func AskOptionListFilter(me AskOptionList, cb AskOptionFilterFunc) AskOptionList {
  202. result := make(AskOptionSlice, 0)
  203. for i := 0; i < me.AskOptionListLen(); i++ {
  204. res := cb(me.AskOptionListGet(i))
  205. if res != nil {
  206. result = append(result, res)
  207. }
  208. }
  209. return result
  210. }
  211. /* Finds the name irrespecful of the case */
  212. func AskOptionListFindName(me AskOptionList, name string) AskOption {
  213. return AskOptionListEach(me, func(e AskOption) AskOption {
  214. if strings.ToLower(e.AskName()) == strings.ToLower(name) {
  215. return e
  216. } else {
  217. return nil
  218. }
  219. })
  220. }
  221. /* Filters the list by privilege level (only those allowed by the level are retained) */
  222. func AskOptionListFilterPrivilege(me AskOptionList, privilege world.Privilege) AskOptionList {
  223. return AskOptionListFilter(me, func(e AskOption) AskOption {
  224. if e.AskPrivilege() <= privilege {
  225. return e
  226. } else {
  227. return nil
  228. }
  229. })
  230. }
  231. type MergedAskOptionList struct {
  232. head AskOptionList
  233. tail AskOptionList
  234. }
  235. func (me *MergedAskOptionList) AskOptionListLen() int {
  236. return me.head.AskOptionListLen() + me.tail.AskOptionListLen()
  237. }
  238. func (me *MergedAskOptionList) AskOptionListGet(index int) AskOption {
  239. headlen := me.head.AskOptionListLen()
  240. if index < headlen {
  241. return me.head.AskOptionListGet(index)
  242. }
  243. return me.tail.AskOptionListGet(index - headlen)
  244. }
  245. /* Merges two AskOptionLists without copying using the MergedAskOptionList rtype */
  246. func AskOptionListMerge(me AskOptionList, you AskOptionList) AskOptionList {
  247. return &MergedAskOptionList{me, you}
  248. }
  249. func (me AskOptionSlice) Each(cb AskOptionFilterFunc) AskOption {
  250. for i := 0; i < len(me); i++ {
  251. res := cb(me[i])
  252. if res != nil {
  253. return res
  254. }
  255. }
  256. return nil
  257. }
  258. func (me AskOptionSlice) Filter(cb AskOptionFilterFunc) AskOptionSlice {
  259. result := make(AskOptionSlice, 0)
  260. for i := 0; i < len(me); i++ {
  261. res := cb(me[i])
  262. if res != nil {
  263. result = append(result, res)
  264. }
  265. }
  266. return result
  267. }
  268. /* Finds the name irrespecful of the case */
  269. func (me AskOptionSlice) FindName(name string) AskOption {
  270. return me.Each(func(e AskOption) AskOption {
  271. if strings.ToLower(e.AskName()) == strings.ToLower(name) {
  272. return e
  273. } else {
  274. return nil
  275. }
  276. })
  277. }
  278. /* Filters the list by privilege level (only those allowed by the level are retained) */
  279. func (me AskOptionSlice) FilterPrivilege(privilege world.Privilege) AskOptionSlice {
  280. return me.Filter(func(e AskOption) AskOption {
  281. if e.AskPrivilege() <= privilege {
  282. return e
  283. } else {
  284. return nil
  285. }
  286. })
  287. }
  288. func (me *Client) AskOptionListHelp(alist AskOptionList, input []byte) {
  289. re := regexp.MustCompile("[^ \t,]+")
  290. argv := re.FindAll(input, -1)
  291. if len(argv) < 2 {
  292. me.Printf("Help usage: help <topic>.\n")
  293. return
  294. }
  295. e := AskOptionListFindName(alist, string(argv[1]))
  296. if e == nil {
  297. me.Printf("Cannot find topic %s in list. No help available.\n", string(argv[1]))
  298. } else {
  299. al := e.AskLong()
  300. if al == "" {
  301. me.Printf("Topic %s found, but help is unavailable.\n", string(argv[1]))
  302. } else {
  303. me.Printf("Help on %s:\n%s\n", string(argv[1]), e.AskLong())
  304. }
  305. }
  306. }
  307. func (me *Client) AskOptionListOnce(heading string, prompt string, noecho bool, alist AskOptionList) (result AskOption) {
  308. list := AskOptionListFilterPrivilege(alist, me.account.Privilege)
  309. me.Printf("\n%s\n\n", heading)
  310. for i := 0; i < list.AskOptionListLen(); i++ {
  311. v := list.AskOptionListGet(i)
  312. sh := v.AskShort()
  313. if sh == "" {
  314. me.Printf("[%d] %s\n", i+1, v.AskName())
  315. } else {
  316. me.Printf("[%d] %s: %s\n", i+1, v.AskName(), sh)
  317. }
  318. }
  319. me.Printf("\n")
  320. aid := me.AskSomething(prompt, "", "", false)
  321. iresp, err := strconv.Atoi(string(aid))
  322. if err != nil { /* Try by name if not a number. */
  323. e := AskOptionListFindName(alist, string(aid))
  324. if e != nil {
  325. return e
  326. } else if ok, _ := regexp.Match("help", bytes.ToLower(aid)); ok {
  327. me.AskOptionListHelp(list, aid)
  328. } else {
  329. me.Printf("Name not found in list. Please choose a number or name from the list above. Or type help <option> for help on that option.\n")
  330. }
  331. } else if (iresp > 0) && (iresp <= list.AskOptionListLen()) {
  332. /* In range of list. */
  333. return list.AskOptionListGet(iresp - 1)
  334. } else {
  335. me.Printf("Please choose a number or name from the list above.\n")
  336. }
  337. return nil
  338. }
  339. func (me *Client) AskOptionList(
  340. heading string, prompt string, noecho bool,
  341. noconfirm bool, list AskOptionList) (result AskOption) {
  342. for {
  343. result = me.AskOptionListOnce(heading, prompt, noecho, list)
  344. if result != nil {
  345. if noconfirm || me.AskYesNo(heading+"\nConfirm "+result.AskName()+"? ") {
  346. return result
  347. }
  348. }
  349. }
  350. }
  351. func (me *Client) AskOptionListExtra(heading string,
  352. prompt string, noecho bool, noconfirm bool, list AskOptionList,
  353. extra AskOptionList) (result AskOption) {
  354. xlist := AskOptionListMerge(list, extra)
  355. return me.AskOptionList(heading, prompt, noecho, noconfirm, xlist)
  356. }
  357. func (me *Client) AskEntityListOnce(
  358. heading string, prompt string, noecho bool,
  359. elist world.EntitylikeSlice, extras []string) (result world.Entitylike, alternative string) {
  360. list := elist.FilterPrivilege(me.account.Privilege)
  361. me.Printf("\n%s\n\n", heading)
  362. last := 0
  363. for i, v := range list {
  364. e := v.AsEntity()
  365. me.Printf("[%d] %s: %s\n", i+1, e.Name, e.Short)
  366. last = i + 1
  367. }
  368. if extras != nil {
  369. for i, v := range extras {
  370. me.Printf("[%d] %s\n", last+i+1, v)
  371. }
  372. }
  373. me.Printf("\n")
  374. aid := me.AskSomething(prompt, "", "", false)
  375. iresp, err := strconv.Atoi(string(aid))
  376. if err != nil { /* Try by name if not a number. */
  377. e := list.FindName(string(aid))
  378. if e != nil {
  379. return e, ""
  380. } else {
  381. if extras != nil {
  382. for _, v := range extras {
  383. if strings.ToLower(v) == strings.ToLower(string(aid)) {
  384. return nil, v
  385. }
  386. }
  387. }
  388. me.Printf("Name not found in list. Please choose a number or name from the list above.\n")
  389. }
  390. } else if (iresp > 0) && (iresp <= len(list)) { /* In range of list. */
  391. return list[iresp-1], ""
  392. } else if (extras != nil) && (iresp > last) && (iresp <= last+len(extras)) {
  393. return nil, extras[iresp-last-1]
  394. } else {
  395. me.Printf("Please choose a number or name from the list above.\n")
  396. }
  397. return nil, ""
  398. }
  399. func (me *Client) AskEntityList(
  400. heading string, prompt string, noecho bool,
  401. noconfirm bool, list world.EntitylikeSlice, extras []string) (result world.Entitylike, alternative string) {
  402. for {
  403. result, alternative = me.AskEntityListOnce(heading, prompt, noecho, list, extras)
  404. if result != nil {
  405. e := result.AsEntity()
  406. if !noconfirm {
  407. me.Printf("\n%s: %s\n\n%s\n\n", e.Name, e.Short, e.Long)
  408. }
  409. if noconfirm || me.AskYesNo(heading+"\nConfirm "+e.Name+"? ") {
  410. return result, ""
  411. }
  412. } else if alternative != "" {
  413. if noconfirm || me.AskYesNo("Confirm "+alternative+" ?") {
  414. return result, alternative
  415. }
  416. }
  417. }
  418. }
  419. const LOGIN_RE = "^[A-Za-z][A-Za-z0-9]*$"
  420. func (me *Client) AskLogin() []byte {
  421. return me.AskSomething("Login?>", LOGIN_RE, "Login must consist of letters followed by letters or numbers.", false)
  422. }
  423. const EMAIL_RE = "@"
  424. func (me *Client) AskEmail() []byte {
  425. return me.AskSomething("E-mail?>", EMAIL_RE, "Email must have at least an @ in there somewhere.", false)
  426. }
  427. const CHARNAME_RE = "^[A-Z][A-Za-z]+$"
  428. func (me *Client) AskCharacterName() []byte {
  429. return me.AskSomething("Character Name?>", CHARNAME_RE, "Character name consist of a capital letter followed by at least one letter.", false)
  430. }
  431. func (me *Client) AskPassword() []byte {
  432. return me.AskSomething("Password?>", "", "", true)
  433. }
  434. func (me *Client) AskRepeatPassword() []byte {
  435. return me.AskSomething("Repeat Password?>", "", "", true)
  436. }
  437. func (me *Client) HandleCommand() {
  438. command := me.ReadCommand()
  439. me.ProcessCommand(command)
  440. }