profile.go 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. // This file is subject to a 1-clause BSD license.
  2. // Its contents can be found in the enclosed LICENSE file.
  3. // Package irc defines some utility types and functions for an IRC bot.
  4. package irc
  5. import (
  6. "strings"
  7. "sync"
  8. "notabug.org/mouz/bot/app/util"
  9. )
  10. // Profile defines bot configuration data.
  11. type Profile interface {
  12. // Root defines the root directory with the bot's configuration data.
  13. Root() string
  14. // Channels yields all channels the bot should join on startup.
  15. Channels() []Channel
  16. // Address defines the host and port of the server/network to connect to.
  17. Address() string
  18. // Nickname yields the bot's nickname.
  19. Nickname() string
  20. // SetNickname sets the bot's nickname. This is generally only called
  21. // when the bot logs in and finds its name alredy in use. If the nick
  22. // can not be regained, this function is used to alter it to something
  23. // which is still available.
  24. SetNickname(string)
  25. // NickservPassword defines the bot's nickserv password. This will be
  26. // used to register the bot when it logs in. It is only relevant if the
  27. // bot has a registered nickname and nickserv exists on the server.
  28. NickservPassword() string
  29. // SetNickservPassword sets the bot's nickserv password. This is be
  30. // used to register the bot when it logs in. It is only relevant if the
  31. // bot has a registered nickname and nickserv exists on the server.
  32. SetNickservPassword(string)
  33. // CommandPrefix this is the prefix used for all bot commands. Whenever
  34. // the bot reads incoming PRIVMSG data, it looks for this prefix to
  35. // determine if a command call was issued or not.
  36. CommandPrefix() string
  37. // Save saves the profile to disk.
  38. Save() error
  39. // Load loads the profile from disk.
  40. Load() error
  41. // IsWhitelisted returns true if the given hostmask is in the whitelist.
  42. // This means the user to whom it belongs is allowed to execute restricted
  43. // commands. This performs a case-insensitive comparison.
  44. IsWhitelisted(string) bool
  45. // Whitelist returns a copy of the current whitelist.
  46. Whitelist() []string
  47. // WhitelistAdd adds the given hostmask to the whitelist,
  48. // provided it does not already exist.
  49. WhitelistAdd(string)
  50. // WhitelistRemove removes the given hostmask from the whitelist,
  51. // provided it exists.
  52. WhitelistRemove(string)
  53. // IsMe returns true if the given name equals the bot's nickname.
  54. // This is used in request handlers to quickly check if a request
  55. // is targeted specifically at this bot or not.
  56. IsMe(string) bool
  57. // ForkArgs returns a list of command line arguments which should be
  58. // passed to a forked child process.
  59. ForkArgs() []string
  60. }
  61. // profile defines bot configuration data.
  62. //
  63. // The fields are embedded in a sub struct to differentiate them from the
  64. // method names needed to qualify as a Profile interface. I would rather
  65. // just make these field names lower case, but Go's JSON decoder will not
  66. // work on non-exported fields. Thus breaking the Load/Save functionality.
  67. type profile struct {
  68. m sync.RWMutex
  69. root string
  70. data profileData
  71. }
  72. // profileData defines the parts of the profile which are saved to
  73. // an external configuration file.
  74. type profileData struct {
  75. Whitelist []string
  76. Channels []Channel
  77. Address string
  78. Nickname string
  79. NickservPassword string
  80. CommandPrefix string
  81. }
  82. // NewProfile creates a new profile for the given root directory.
  83. func NewProfile(root string) Profile {
  84. return &profile{
  85. root: root,
  86. data: profileData{
  87. Address: "server.net:6667",
  88. Nickname: "bot_name",
  89. Channels: []Channel{
  90. {Name: "#test_channel"},
  91. },
  92. Whitelist: []string{
  93. "~user@server.com",
  94. },
  95. CommandPrefix: "!",
  96. },
  97. }
  98. }
  99. func (p *profile) Root() string {
  100. p.m.RLock()
  101. defer p.m.RUnlock()
  102. return p.root
  103. }
  104. func (p *profile) ForkArgs() []string {
  105. p.m.RLock()
  106. defer p.m.RUnlock()
  107. return []string{p.root}
  108. }
  109. func (p *profile) Channels() []Channel {
  110. p.m.RLock()
  111. defer p.m.RUnlock()
  112. return p.data.Channels
  113. }
  114. func (p *profile) Address() string {
  115. p.m.RLock()
  116. defer p.m.RUnlock()
  117. return p.data.Address
  118. }
  119. func (p *profile) Nickname() string {
  120. p.m.RLock()
  121. defer p.m.RUnlock()
  122. return p.data.Nickname
  123. }
  124. func (p *profile) SetNickname(v string) {
  125. p.m.Lock()
  126. p.data.Nickname = v
  127. p.m.Unlock()
  128. p.Save()
  129. }
  130. func (p *profile) NickservPassword() string {
  131. p.m.RLock()
  132. defer p.m.RUnlock()
  133. return p.data.NickservPassword
  134. }
  135. func (p *profile) SetNickservPassword(v string) {
  136. p.m.Lock()
  137. p.data.NickservPassword = v
  138. p.m.Unlock()
  139. p.Save()
  140. }
  141. func (p *profile) CommandPrefix() string {
  142. p.m.RLock()
  143. defer p.m.RUnlock()
  144. return p.data.CommandPrefix
  145. }
  146. func (p *profile) Whitelist() []string {
  147. p.m.RLock()
  148. defer p.m.RUnlock()
  149. out := make([]string, len(p.data.Whitelist))
  150. copy(out, p.data.Whitelist)
  151. return out
  152. }
  153. func (p *profile) WhitelistAdd(mask string) {
  154. p.m.Lock()
  155. for _, str := range p.data.Whitelist {
  156. if strings.EqualFold(str, mask) {
  157. p.m.Unlock()
  158. return
  159. }
  160. }
  161. p.data.Whitelist = append(p.data.Whitelist, mask)
  162. p.m.Unlock()
  163. p.Save()
  164. }
  165. func (p *profile) WhitelistRemove(mask string) {
  166. p.m.Lock()
  167. for i, str := range p.data.Whitelist {
  168. if !strings.EqualFold(str, mask) {
  169. continue
  170. }
  171. copy(p.data.Whitelist[i:], p.data.Whitelist[i+1:])
  172. p.data.Whitelist = p.data.Whitelist[:len(p.data.Whitelist)-1]
  173. break
  174. }
  175. p.m.Unlock()
  176. p.Save()
  177. }
  178. func (p *profile) IsWhitelisted(mask string) bool {
  179. p.m.RLock()
  180. defer p.m.RUnlock()
  181. for _, str := range p.data.Whitelist {
  182. if strings.EqualFold(str, mask) {
  183. return true
  184. }
  185. }
  186. return false
  187. }
  188. func (p *profile) IsMe(name string) bool {
  189. p.m.RLock()
  190. defer p.m.RUnlock()
  191. return strings.EqualFold(p.data.Nickname, name)
  192. }
  193. func (p *profile) Save() error {
  194. p.m.RLock()
  195. err := util.WriteFile("profile.cfg", p.data, false)
  196. p.m.RUnlock()
  197. return err
  198. }
  199. func (p *profile) Load() error {
  200. p.m.Lock()
  201. err := util.ReadFile("profile.cfg", &p.data, false)
  202. p.m.Unlock()
  203. return err
  204. }