formatter_everyone.go 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. // mautrix-discord - A Matrix-Discord puppeting bridge.
  2. // Copyright (C) 2023 Tulir Asokan
  3. //
  4. // This program is free software: you can redistribute it and/or modify
  5. // it under the terms of the GNU Affero General Public License as published by
  6. // the Free Software Foundation, either version 3 of the License, or
  7. // (at your option) any later version.
  8. //
  9. // This program is distributed in the hope that it will be useful,
  10. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. // GNU Affero General Public License for more details.
  13. //
  14. // You should have received a copy of the GNU Affero General Public License
  15. // along with this program. If not, see <https://www.gnu.org/licenses/>.
  16. package main
  17. import (
  18. "fmt"
  19. "regexp"
  20. "github.com/yuin/goldmark"
  21. "github.com/yuin/goldmark/ast"
  22. "github.com/yuin/goldmark/parser"
  23. "github.com/yuin/goldmark/renderer"
  24. "github.com/yuin/goldmark/text"
  25. "github.com/yuin/goldmark/util"
  26. )
  27. type astDiscordEveryone struct {
  28. ast.BaseInline
  29. onlyHere bool
  30. }
  31. var _ ast.Node = (*astDiscordEveryone)(nil)
  32. var astKindDiscordEveryone = ast.NewNodeKind("DiscordEveryone")
  33. func (n *astDiscordEveryone) Dump(source []byte, level int) {
  34. ast.DumpHelper(n, source, level, nil, nil)
  35. }
  36. func (n *astDiscordEveryone) Kind() ast.NodeKind {
  37. return astKindDiscordEveryone
  38. }
  39. func (n *astDiscordEveryone) String() string {
  40. if n.onlyHere {
  41. return "@here"
  42. }
  43. return "@everyone"
  44. }
  45. type discordEveryoneParser struct{}
  46. var discordEveryoneRegex = regexp.MustCompile(`@(everyone|here)`)
  47. var defaultDiscordEveryoneParser = &discordEveryoneParser{}
  48. func (s *discordEveryoneParser) Trigger() []byte {
  49. return []byte{'@'}
  50. }
  51. func (s *discordEveryoneParser) Parse(parent ast.Node, block text.Reader, pc parser.Context) ast.Node {
  52. line, _ := block.PeekLine()
  53. match := discordEveryoneRegex.FindSubmatch(line)
  54. if match == nil {
  55. return nil
  56. }
  57. block.Advance(len(match[0]))
  58. return &astDiscordEveryone{
  59. onlyHere: string(match[1]) == "here",
  60. }
  61. }
  62. func (s *discordEveryoneParser) CloseBlock(parent ast.Node, pc parser.Context) {
  63. // nothing to do
  64. }
  65. type discordEveryoneHTMLRenderer struct{}
  66. func (r *discordEveryoneHTMLRenderer) RegisterFuncs(reg renderer.NodeRendererFuncRegisterer) {
  67. reg.Register(astKindDiscordEveryone, r.renderDiscordEveryone)
  68. }
  69. func (r *discordEveryoneHTMLRenderer) renderDiscordEveryone(w util.BufWriter, source []byte, n ast.Node, entering bool) (status ast.WalkStatus, err error) {
  70. status = ast.WalkContinue
  71. if !entering {
  72. return
  73. }
  74. mention, _ := n.(*astDiscordEveryone)
  75. class := "everyone"
  76. if mention != nil && mention.onlyHere {
  77. class = "here"
  78. }
  79. _, _ = fmt.Fprintf(w, `<span class="discord-mention-%s">@room</span>`, class)
  80. return
  81. }
  82. type discordEveryone struct{}
  83. var ExtDiscordEveryone = &discordEveryone{}
  84. func (e *discordEveryone) Extend(m goldmark.Markdown) {
  85. m.Parser().AddOptions(parser.WithInlineParsers(
  86. util.Prioritized(defaultDiscordEveryoneParser, 600),
  87. ))
  88. m.Renderer().AddOptions(renderer.WithNodeRenderers(
  89. util.Prioritized(&discordEveryoneHTMLRenderer{}, 600),
  90. ))
  91. }