member.rb 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. require './app/commands/base_command.rb'
  2. class MemberCommand < BaseCommand
  3. def self.opts
  4. {
  5. # Nav consists of reaction sections and descriptions
  6. nav: {
  7. all: [ Emoji::EYES, "View all info about the character" ],
  8. image: [ Emoji::PICTURE, "Scroll though the character's images" ],
  9. journal: [ Emoji::NOTEBOOK, "Scroll though pages of journal entries" ],
  10. bags: [ Emoji::BAGS, "View the character's inventory" ],
  11. family: [ Emoji::FAMILY, "View related characters" ],
  12. forms: [ Emoji::SPY, "View alternative forms of the character" ],
  13. user: [ Emoji::BUST, "View the writer's other characters in a list" ]
  14. },
  15. # Usage has each option, in order with instructions, and a real example
  16. usage: {
  17. name:
  18. "Searches characters for the specified name, or discord user. " +
  19. "If no name is given, R0ry will show a list of all characters",
  20. section:
  21. "Skips to the specified section, some options include: bio, type, status, " +
  22. "rumors, image, bags. If no section is given, R0ry will default to history",
  23. keyword:
  24. "Displays a specific image or journal, searched by its title, or keyword. " +
  25. "Can only be used if the section option is `image` or `journal`",
  26. }
  27. }
  28. end
  29. def self.cmd
  30. desc = "Display info about the guild members"
  31. @cmd ||= Command.new(:member, desc, opts) do |event, name, section, keyword|
  32. # Determine display type: user, character, or list
  33. case name
  34. # Show user's character list
  35. when UID
  36. # Find User to display, and a list of their characters
  37. member = event.server.member(UID.match(name)[1])
  38. characters = Character.where(user_id: UID.match(name)[1])
  39. active_chars = characters.filter{ |c| c.active == 'Active' }
  40. # Handle sfw channels and nsfw characters
  41. sfw = !event.channel.nsfw?
  42. sfw_chars = active_chars.filter{ |c| c.rating != 'NSFW' }
  43. chars = sfw ? sfw_chars : active_chars
  44. # Generate embed and reply
  45. BotResponse.new(
  46. embed: user_char_embed(characters, member, sfw),
  47. carousel: active_chars.map(&:id),
  48. reactions: Emoji::NUMBERS.take(chars.count).push(Emoji::CROSS)
  49. )
  50. # Show Character List Embed
  51. when nil
  52. # Grab list of active characters, and types
  53. characters = Character.where(active: 'Active').order(:name)
  54. types = Type.all
  55. # Create reaction list
  56. reactions = Emoji::NUMBERS.take(4)
  57. # Generate embed, and reply
  58. BotResponse.new(
  59. embed: char_list_embed(characters, 'active', types),
  60. reactions: reactions.push(Emoji::CROSS),
  61. carousel: 'Guild'
  62. )
  63. # Show character embed
  64. else
  65. # Find Character
  66. if name.to_i > 0
  67. character = Character.find(name)
  68. elsif section&.match(/deleted?/i)
  69. character = Character.where(active: 'Deleted')
  70. .where('name ilike ?', name)
  71. else
  72. character = Character.where.not(active: 'Deleted')
  73. .where('name ilike ?', name)
  74. .or(Character.where.not(active: 'Deleted')
  75. .where('? = any(aliases)', name))
  76. raise 'Character not found!' if character.empty?
  77. end
  78. char_reply(event, character, section, keyword)
  79. end
  80. rescue ActiveRecord::RecordNotFound => e
  81. error_embed("Record Not Found!", e.message)
  82. #rescue StandardError => e
  83. #error_embed(e.message)
  84. end
  85. end
  86. def self.char_reply(event, character, section, keyword)
  87. # Current channel restricted?
  88. sfw = !event.channel.nsfw?
  89. # Determine if duplicate characters, then filter NSFW if SFW channel
  90. unless character.is_a? Character
  91. chars = sfw ? character.filter{ |c| c.rating != 'NSFW' } : character
  92. # If still more than 1 character, reply with duplicate embed
  93. if chars.length > 1
  94. embed = dup_char_embed(chars, chars.first.name)
  95. return BotResponse.new(
  96. embed: embed,
  97. reactions: Emoji::NUMBERS.take(chars.count),
  98. carousel: chars.map(&:id)
  99. )
  100. elsif chars.length == 0
  101. nsfw_char_embed(chars.first, event)
  102. else
  103. character = character.first
  104. end
  105. end
  106. case section
  107. when /image/i
  108. # Find image if specified
  109. image = CharImage.where(char_id: character.id).
  110. find_by('keyword ilike ?', keyword || 'Default')
  111. raise 'Image not found!' if keyword && !image
  112. when /journal/i
  113. # Find journal if specified
  114. if keyword
  115. journal = JournalEntry.where(char_id: character.id).
  116. find_by('title ilike ?', keyword)
  117. raise 'Journal not found!' if !journal
  118. else
  119. # Fetch Journal list if no keyword
  120. journal = JournalEntry.where(char_id: character.id).take(10)
  121. end
  122. end
  123. # Ensure the content is appropriate for the current channel
  124. if sfw && ( image&.category == 'NSFW' || character.rating == 'NSFW' )
  125. return nsfw_char_embed(character, event)
  126. end
  127. # Generate Character Embed
  128. embed = character_embed(
  129. character: character,
  130. event: event,
  131. section: section,
  132. image: image,
  133. journal: journal
  134. )
  135. # Determine Carousel Type and create reply
  136. if section&.match(/images?/i)
  137. BotResponse.new(
  138. embed: embed,
  139. carousel: image,
  140. reactions: ImageCarousel.sections.map{ |k,v| k }.push(Emoji::CROSS)
  141. )
  142. elsif section&.match(/journal/i)
  143. journal = journal.first unless journal.is_a? JournalEntry
  144. BotResponse.new(
  145. embed: embed,
  146. carousel: journal,
  147. reactions: JournalCarousel.sections.map{ |k,v| k }.push(Emoji::CROSS)
  148. )
  149. elsif section&.match(/(alt(ernate)?)?\s?forms?/i)
  150. chars = []
  151. if character.alt_form
  152. chars.push( Character.find(character.alt_form) )
  153. else
  154. chars.push(character)
  155. end
  156. # Add forms
  157. chars.concat( Character.where(alt_form: chars.first.id) )
  158. BotResponse.new(
  159. embed: embed,
  160. carousel: chars.map{ |c| c.id },
  161. reactions: Emoji::NUMBERS.take(chars.length).push(Emoji::CROSS)
  162. )
  163. else
  164. BotResponse.new(
  165. embed: embed,
  166. carousel: character,
  167. reactions: CharacterCarousel.sections.map{ |k,v| k }.push(Emoji::CROSS)
  168. )
  169. end
  170. end
  171. def self.example_command(event=nil)
  172. sections = ['all', 'bio', 'type', 'status', 'rumors', 'image', 'bags', 'journal', 'forms']
  173. case ['', 'user', 'name', 'section', 'keyword'].sample
  174. when ''
  175. []
  176. when 'user'
  177. user = Character.where(active: 'Active').order('RANDOM()').first.user_id
  178. member = event&.server&.member(user)
  179. ["@#{member&.nickname || member&.name || 'user_name'}"]
  180. when 'name'
  181. [Character.where.not(active: 'Deleted').order('RANDOM()').first.name]
  182. when 'section'
  183. [Character.where.not(active: 'Deleted').order('RANDOM()').first.name,
  184. sections.sample]
  185. when 'keyword'
  186. i = CharImage.where.not(keyword: 'Default').order('RANDOM()').first
  187. j = JournalEntry.order('RANDOM()').first
  188. [[Character.find(i.char_id).name, 'image', i.keyword],
  189. [Character.find(j.char_id).name, 'journal', j.title || j.date]].sample
  190. end
  191. end
  192. end