Mirror of the official squirssi repository.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

398 lines
11 KiB

  1. package squirssi
  2. import (
  3. "fmt"
  4. "strings"
  5. "time"
  6. ui "github.com/gizak/termui/v3"
  7. "github.com/sirupsen/logrus"
  8. )
  9. func padLeft(s string, padTo int) string {
  10. ml := len(s)
  11. if ml > padTo {
  12. return s[:padTo-1] + string(ui.ELLIPSES)
  13. } else if ml < padTo {
  14. return strings.Repeat(" ", padTo-ml) + s
  15. }
  16. return s
  17. }
  18. func padRight(s string, padTo int) string {
  19. ml := len(s)
  20. if ml > padTo {
  21. return s[:padTo-1] + string(ui.ELLIPSES)
  22. } else if ml < padTo {
  23. return s + strings.Repeat(" ", padTo-ml)
  24. }
  25. return s
  26. }
  27. func padLeftStyled(s StyledString, padTo int) string {
  28. ml := len(s.string)
  29. res := s.string
  30. if ml > padTo {
  31. res = s.string[:padTo-1] + string(ui.ELLIPSES)
  32. } else if ml < padTo {
  33. res = strings.Repeat(" ", padTo-ml) + s.string
  34. }
  35. if s.Style != "" {
  36. return "[" + res + "](" + s.Style + ")"
  37. }
  38. return res
  39. }
  40. type Message struct {
  41. string
  42. mine bool
  43. refsMe bool
  44. }
  45. func (m Message) String() string {
  46. if m.mine {
  47. return "[" + m.string + "](fg:gray100)"
  48. } else if m.refsMe {
  49. return "[" + m.string + "](mod:bold)"
  50. }
  51. return m.string
  52. }
  53. func MyMessage(m string) Message {
  54. return Message{m, true, false}
  55. }
  56. func SomeMessage(m string, myNick Nick) Message {
  57. if strings.Contains(m, myNick.string) {
  58. return Message{m, false, true}
  59. }
  60. return Message{m, false, false}
  61. }
  62. type Nick struct {
  63. string
  64. me bool
  65. }
  66. type Target struct {
  67. Nick
  68. Me Nick
  69. }
  70. func (n Target) IsChannel() bool {
  71. return len(n.string) > 0 && n.string[0] == '#'
  72. }
  73. func SomeTarget(name string, me string) Target {
  74. if me == name {
  75. return Target{MyNick(name), MyNick(me)}
  76. }
  77. return Target{SomeNick(name), MyNick(me)}
  78. }
  79. func (n Nick) String() string {
  80. if n.me {
  81. return "[" + n.string + "](mod:bold)"
  82. }
  83. return "[" + n.string + "](mod:none)"
  84. }
  85. func (n Nick) Styled() StyledString {
  86. if n.me {
  87. return StyledString{string: n.string, Style: "mod:bold"}
  88. }
  89. return StyledString{string: n.string, Style: "mod:none"}
  90. }
  91. func MyNick(nick string) Nick {
  92. return Nick{nick, true}
  93. }
  94. func SomeNick(nick string) Nick {
  95. return Nick{nick, false}
  96. }
  97. type StyledString struct {
  98. string
  99. Style string
  100. }
  101. func Unstyled(s string) StyledString {
  102. return StyledString{string: s}
  103. }
  104. func Styled(s, style string) StyledString {
  105. return StyledString{string: s, Style: style}
  106. }
  107. func (s StyledString) String() string {
  108. if s.Style != "" {
  109. return "[" + s.string + "](" + s.Style + ")"
  110. }
  111. return s.string
  112. }
  113. var basePrefix = Unstyled("* ")
  114. func WritePrefixed(win Window, prefix StyledString, message string) error {
  115. padding := padLeftStyled(prefix, win.padding())
  116. _, err := win.WriteString(fmt.Sprintf("%s[│](fg:grey) %s", padding, message))
  117. return err
  118. }
  119. func WriteQuit(wm *WindowManager, nick Nick, message string) {
  120. wins := wm.Windows()
  121. for _, win := range wins {
  122. if nick.me {
  123. if err := WritePrefixed(win, basePrefix, fmt.Sprintf("Quit: %s", message)); err != nil {
  124. logrus.Warnf("%s: failed to write user quit: %s", win.Title(), err)
  125. }
  126. continue
  127. }
  128. if win.Title() == nick.string {
  129. // direct message with nick, update title and print there
  130. if err := WritePrefixed(win, basePrefix, fmt.Sprintf("%s quit (%s)", nick, message)); err != nil {
  131. logrus.Warnf("%s: failed to write user quit: %s", win.Title(), err)
  132. }
  133. } else if ch, ok := win.(*Channel); ok {
  134. if ch.DeleteUser(nick.string) {
  135. if err := WritePrefixed(win, basePrefix, fmt.Sprintf("%s quit (%s)", nick, message)); err != nil {
  136. logrus.Warnf("%s: failed to write user quit: %s", win.Title(), err)
  137. }
  138. }
  139. }
  140. }
  141. }
  142. func WriteNick(wm *WindowManager, nick Nick, newNick Nick) {
  143. wins := wm.Windows()
  144. for _, win := range wins {
  145. if win.Title() == nick.string {
  146. // direct message with nick, update title and print there
  147. if dm, ok := win.(*DirectMessage); ok {
  148. dm.mu.Lock()
  149. dm.name = newNick.string
  150. dm.mu.Unlock()
  151. }
  152. if err := WritePrefixed(win, basePrefix, fmt.Sprintf("%s is now known as %s", nick.String(), newNick)); err != nil {
  153. logrus.Warnf("%s: failed to write nick change: %s", win.Title(), err)
  154. }
  155. } else if ch, ok := win.(*Channel); ok {
  156. if ch.UpdateUser(nick.string, newNick.string) {
  157. if err := WritePrefixed(win, basePrefix, fmt.Sprintf("%s is now known as %s", nick, newNick)); err != nil {
  158. logrus.Warnf("%s: failed to write nick change: %s", win.Title(), err)
  159. }
  160. }
  161. } else if win.Title() == "status" && nick.me {
  162. if err := WritePrefixed(win, basePrefix, fmt.Sprintf("You are now known as %s", newNick)); err != nil {
  163. logrus.Warnf("%s: failed to write nick change: %s", win.Title(), err)
  164. }
  165. }
  166. }
  167. }
  168. func WriteWhois(win Window, nick string, args []string) {
  169. m := strings.Join(args, " ")
  170. if win == nil {
  171. logrus.Infof("WHOIS %s => %s", nick, m)
  172. return
  173. }
  174. if err := WritePrefixed(win, basePrefix, fmt.Sprintf("WHOIS => %s", m)); err != nil {
  175. logrus.Warnf("%s: failed to write whois result message: %s", win.Title(), err)
  176. }
  177. }
  178. func WriteError(win Window, name, message string) {
  179. if win == nil {
  180. logrus.Errorf("%s: %s", name, message)
  181. return
  182. }
  183. if err := WritePrefixed(win, basePrefix, fmt.Sprintf("%s: %s", name, message)); err != nil {
  184. logrus.Warnf("%s: failed to write error message: %s", win.Title(), err)
  185. }
  186. }
  187. func WriteRaw(win Window, raw string) {
  188. if err := WritePrefixed(win, Unstyled("RAW"), raw); err != nil {
  189. logrus.Warnf("%s: failed to write raw command: %s", win.Title(), err)
  190. }
  191. }
  192. func WriteMessage(win Window, message string) {
  193. if err := WritePrefixed(win, basePrefix, fmt.Sprintf("[%s](fg:red4)", message)); err != nil {
  194. logrus.Warnf("%s: failed to write message: %s", win.Title(), err)
  195. }
  196. }
  197. func WriteEval(win Window, script string) {
  198. if err := WritePrefixed(win, Styled("EVAL", "fg:orange"), fmt.Sprintf("[>](fg:grey100,mod:bold) %s", script)); err != nil {
  199. logrus.Warnf("%s: failed to write eval command: %s", win.Title(), err)
  200. }
  201. }
  202. func WriteEvalResult(win Window, script string) {
  203. if err := WritePrefixed(win, Styled("EVAL", "fg:orange"), fmt.Sprintf("[=](fg:grey100,mod:bold) %s", script)); err != nil {
  204. logrus.Warnf("%s: failed to write eval result: %s", win.Title(), err)
  205. }
  206. }
  207. func WriteEvalError(win Window, script string) {
  208. if err := WritePrefixed(win, Styled("EVAL", "fg:orange"), fmt.Sprintf("[!](fg:red,mod:bold) %s", script)); err != nil {
  209. logrus.Warnf("%s: failed to write eval error: %s", win.Title(), err)
  210. }
  211. }
  212. func Write329(win Window, created time.Time) {
  213. if err := WritePrefixed(win, basePrefix, fmt.Sprintf("Channel created at [%s](mod:bold)", created.String())); err != nil {
  214. logrus.Warnf("%s: failed to write created at message: %s", win.Title(), err)
  215. }
  216. }
  217. func Write331(win Window) {
  218. if err := WritePrefixed(win, basePrefix, fmt.Sprintf("No topic is set in [%s](mod:bold)", win.Title())); err != nil {
  219. logrus.Warnf("%s: failed to write topic message: %s", win.Title(), err)
  220. }
  221. }
  222. func Write332(win Window, topic string) {
  223. if err := WritePrefixed(win, basePrefix, fmt.Sprintf("Topic for [%s](mod:bold) is: %s", win.Title(), topic)); err != nil {
  224. logrus.Warnf("%s: failed to write topic message: %s", win.Title(), err)
  225. }
  226. }
  227. func WriteJoin(win Window, nick Nick) {
  228. if err := WritePrefixed(win, basePrefix, fmt.Sprintf("%s joined [%s](mod:bold)", nick.String(), win.Title())); err != nil {
  229. logrus.Warnf("%s: failed to write join message: %s", win.Title(), err)
  230. }
  231. }
  232. func WriteModes(win Window, modes string) {
  233. if err := WritePrefixed(win, basePrefix, fmt.Sprintf("Modes for [%s](mod:bold): %s", win.Title(), modes)); err != nil {
  234. logrus.Warnf("%s: failed to write modes: %s", win.Title(), err)
  235. }
  236. }
  237. func WriteMode(win Window, nick Nick, mode string) {
  238. if nick.string == "" {
  239. nick.string = "Server"
  240. }
  241. title := win.Title()
  242. if title == "status" {
  243. if err := WritePrefixed(win, basePrefix, fmt.Sprintf("Changed mode for %s (%s)", nick.String(), mode)); err != nil {
  244. logrus.Warnf("%s: failed to write mode message: %s", win.Title(), err)
  245. }
  246. return
  247. }
  248. if err := WritePrefixed(win, basePrefix, fmt.Sprintf("%s changed mode on [%s](mod:bold) (%s)", nick.String(), win.Title(), mode)); err != nil {
  249. logrus.Warnf("%s: failed to write error message: %s", win.Title(), err)
  250. }
  251. }
  252. func WriteTopic(win Window, nick Nick, topic string) {
  253. if ch, ok := win.(*Channel); ok {
  254. ch.mu.Lock()
  255. ch.topic = topic
  256. ch.mu.Unlock()
  257. }
  258. if err := WritePrefixed(win, basePrefix, fmt.Sprintf("%s changed topic on [%s](mod:bold) to: %s", nick.String(), win.Title(), topic)); err != nil {
  259. logrus.Warnf("%s: failed to write topic message: %s", win.Title(), err)
  260. }
  261. }
  262. func WritePart(win Window, nick Nick, message string) {
  263. title := win.Title()
  264. if title == message {
  265. message = ""
  266. } else {
  267. message = " (" + message + ")"
  268. }
  269. if err := WritePrefixed(win, basePrefix, fmt.Sprintf("%s left [%s](mod:bold)%s", nick.String(), title, message)); err != nil {
  270. logrus.Warnf("%s: failed to write part message: %s", win.Title(), err)
  271. }
  272. }
  273. func WriteKick(win Window, kicker Nick, kicked Nick, message string) {
  274. if kicked.string == message {
  275. message = ""
  276. } else {
  277. message = " (" + message + ")"
  278. }
  279. if kicked.me {
  280. win.Notice()
  281. }
  282. if err := WritePrefixed(win, basePrefix, fmt.Sprintf("%s kicked %s from [%s](mod:bold)%s", kicker.String(), kicked.String(), win.Title(), message)); err != nil {
  283. logrus.Warnf("%s: failed to write kick message: %s", win.Title(), err)
  284. }
  285. }
  286. func postProcessMessage(nick *Nick, message *Message) {
  287. if message.refsMe {
  288. nick.me = true
  289. }
  290. }
  291. func WriteAction(win Window, nick Nick, message Message) {
  292. postProcessMessage(&nick, &message)
  293. if message.refsMe || nick.string == win.Title() {
  294. win.Notice()
  295. }
  296. if err := WritePrefixed(win, basePrefix, fmt.Sprintf("%s %s", nick.String(), message.String())); err != nil {
  297. logrus.Warnf("%s: failed to write action message: %s", win.Title(), err)
  298. }
  299. }
  300. func WritePrivmsg(win Window, nick Nick, message Message) {
  301. postProcessMessage(&nick, &message)
  302. if message.refsMe || nick.string == win.Title() {
  303. win.Notice()
  304. }
  305. if err := WritePrefixed(win, nick.Styled(), message.String()); err != nil {
  306. logrus.Warnf("%s: failed to write privmsg: %s", win.Title(), err)
  307. }
  308. }
  309. func WriteHelpGeneric(win Window, msg string) {
  310. prefix := Styled("HELP", "fg:yellow,mod:bold")
  311. if err := WritePrefixed(win, prefix, msg); err != nil {
  312. logrus.Warnf("%s: failed to write help message: %s", win.Title(), err)
  313. }
  314. }
  315. func WriteHelp(win Window, cmd string, desc string) {
  316. cmd = padRight(cmd, 10)
  317. prefix := Styled("HELP", "fg:yellow,mod:bold")
  318. if err := WritePrefixed(win, prefix, fmt.Sprintf("[%s](mod:bold) %s", cmd, desc)); err != nil {
  319. logrus.Warnf("%s: failed to write help message: %s", win.Title(), err)
  320. }
  321. }
  322. func WriteNotice(win Window, target Target, sent bool, message string) {
  323. writeNotice(win, target, "NOTICE", sent, message)
  324. }
  325. func WriteCTCP(win Window, target Target, sent bool, message string) {
  326. writeNotice(win, target, "CTCP", sent, message)
  327. }
  328. func writeNotice(win Window, target Target, kind string, sent bool, message string) {
  329. win.Notice()
  330. if win.Title() == "status" {
  331. arrow := "->"
  332. if sent {
  333. arrow = "<-"
  334. }
  335. prefix := Styled(kind, "fg:grey100,mod:bold")
  336. m := fmt.Sprintf("%s %s %s", target, arrow, message)
  337. if err := WritePrefixed(win, prefix, m); err != nil {
  338. logrus.Warnf("%s: failed to write %s message: %s", win.Title(), strings.ToLower(kind), err)
  339. }
  340. } else {
  341. nick := target.Nick
  342. if sent {
  343. nick = target.Me
  344. }
  345. m := "[" + kind + "](fg:grey100,mod:bold) " + message
  346. if err := WritePrefixed(win, nick.Styled(), m); err != nil {
  347. logrus.Warnf("%s: failed to write %s message: %s", win.Title(), strings.ToLower(kind), err)
  348. }
  349. }
  350. }