瀏覽代碼

add journal entries and navigation options for them

Kylie Jo Swistak 4 年之前
父節點
當前提交
455ab27852

+ 4 - 7
app/carousels/image.rb

@@ -24,7 +24,7 @@ class ImageCarousel < Carousel
       image = CharImage.find(carousel.image_id)
       character = Character.find(image.char_id)
 
-      # Transition into an ImageCarousel
+      # Transition into a MemberCarousel
       event.message.delete_all_reactions
       CharacterCarousel.transition(event, carousel, character)
     when 'left', 'right'
@@ -68,17 +68,14 @@ class ImageCarousel < Carousel
       char_id: nil,
       image_id: image&.id,
       landmark_id: nil,
-      options: nil
+      options: nil,
+      journal_page: nil
     )
 
-    # Array of section reactions and an X
-    img_reactions = sections.map{ |k,v| k }
-    img_reactions.push(Emoji::CROSS)
-
     # Update reply
     BotResponse.new(
       carousel: carousel,
-      reactions: img_reactions,
+      reactions: sections.map{ |k,v| k }.push(Emoji::CROSS),
       embed: character_embed(
         character: character,
         event: event,

+ 84 - 0
app/carousels/journal.rb

@@ -0,0 +1,84 @@
+require './app/models/carousels.rb'
+require './lib/emoji.rb'
+
+class JournalCarousel < Carousel
+  def self.sections
+    {
+      Emoji::UNDO => 'back',
+      Emoji::LEFT => 'left',
+      Emoji::RIGHT => 'right',
+    }
+  end
+
+  def self.update_embed(event, carousel)
+    # Save reactions and determine section
+    reactions = event.message.reactions
+    direction = sections.filter{ |k,v| reactions[k]&.count.to_i > 1 }.values.first
+
+    # Close if X is chosen
+    return carousel.close(event) if reactions[Emoji::CROSS]&.count.to_i > 1
+
+    case direction
+    when 'back'
+      # Fetch character
+      character = Character.find(carousel.char_id)
+
+      # Transition into a MemberCarousel
+      event.message.delete_all_reactions
+      CharacterCarousel.transition(event, carousel, character)
+    when 'left', 'right'
+      # Fetch the correspoding emoji, and remove non-bot reactions
+      emoji = sections.key(direction)
+      event.message.reacted_with(emoji).each do |r|
+        event.message.delete_reaction(r.id, emoji) unless r.current_bot?
+      end
+
+      # Next Journal Page
+      page = JournalController.journal_scroll(
+        char_id: carousel.char_id,
+        page: carousel.journal_page,
+        dir: direction
+      )
+
+      # Update Carousel
+      carousel.update(char_id: carousel.char_id, journal_page: page)
+
+      # Update embed with new page
+      BotResponse.new(
+        carousel: carousel,
+        embed: character_embed(
+          character: Character.find(carousel.char_id),
+          event: event,
+          section: 'journal',
+          journal: JournalController.fetch_page(carousel.char_id, page)
+        )
+      )
+    end
+  end
+
+  def self.transition(event, carousel, character)
+    # Fetch inital page of journals
+    journals = JournalController.fetch_page(character.id, 1)
+
+    # Update carousel to reflect new information
+    carousel.update(
+      char_id: character.id,
+      image_id: nil,
+      landmark_id: nil,
+      options: nil,
+      journal_page: 1
+    )
+
+    # Update Reply
+    BotResponse.new(
+      carousel: carousel,
+      reactions: sections.map{ |k,v| k }.push(Emoji::CROSS),
+      embed: character_embed(
+        character: character,
+        event: event,
+        section: 'journal',
+        journal: journals
+      )
+    )
+  end
+end

+ 7 - 1
app/carousels/member.rb

@@ -3,6 +3,7 @@ class CharacterCarousel < Carousel
     {
       Emoji::EYES => 'all',
       Emoji::PICTURE => 'image',
+      Emoji::NOTEBOOK => 'journal',
       Emoji::BAGS => 'bags',
       Emoji::FAMILY => 'family',
       Emoji::BUST => 'user'
@@ -22,6 +23,10 @@ class CharacterCarousel < Carousel
       # Transition into an ImageCarousel
       event.message.delete_all_reactions
       ImageCarousel.transition(event, carousel, Character.find(carousel.char_id))
+    when 'journal'
+      # Transition into an JournalCarousel
+      event.message.delete_all_reactions
+      JournalCarousel.transition(event, carousel, Character.find(carousel.char_id))
     when 'user'
       # Find User
       character = Character.find(carousel.char_id)
@@ -55,7 +60,8 @@ class CharacterCarousel < Carousel
       char_id: character.id,
       image_id: nil,
       landmark_id: nil,
-      options: nil
+      options: nil,
+      journal_page: nil
     )
 
     # Update reply

+ 2 - 1
app/carousels/user.rb

@@ -32,7 +32,8 @@ class UserCarousel < Carousel
       char_id: nil,
       image_id: nil,
       landmark_id: nil,
-      options: chars.map{ |c| c.id }
+      options: chars.map{ |c| c.id },
+      journal_page: nil
     )
 
     # Array of section reactions and an X

+ 63 - 0
app/commands/journal.rb

@@ -0,0 +1,63 @@
+require './app/commands/base_command.rb'
+
+class JournalCommand < BaseCommand
+  def self.opts
+    {
+      usage: {
+        character: "Searches for the character by name, can only add entries for your own characters",
+        title: "A title for the journal, may be blank (defaults to date)",
+        entry: "The journal entry, should be a paragraph as the character might enter into a diary"
+      }
+    }
+  end
+
+  def self.cmd
+    desc = "Create a short journal entry for a character"
+
+    @cmd ||= Command.new(:journal, desc, opts) do |event, name, title, note|
+      # Find the character
+      character = Character.restricted_find(name, event.author, ['Archived'])
+
+      # Format and create Journal
+      date = Time.now.strftime("%a, %b %d, %Y")
+      if !note
+        note = title
+        title = date
+      elsif title == ''
+        title = date
+      end
+
+      # Create a new Journal Entry with formatted date
+      journal = JournalEntry.create(
+        char_id: character.id,
+        title: title,
+        date: date,
+        entry: note
+      )
+
+      # Create response embed and reply
+      BotResponse.new(
+        embed: character_embed(
+          character: character,
+          event: event,
+          section: :journal,
+          journal: journal
+        )
+      )
+
+    rescue ActiveRecord::RecordNotFound => e
+      error_embed("Record not Found!", e.message)
+    end
+  end
+
+  def self.example_command(event)
+    journal_entry_examples = [
+      "Today I did a thing, and it was fun. Yay!",
+      "As I walk through the valley where I harvest my grain, I take a look at my wife and realize she's very plain",
+      "I want to kill a mother fucker just to see how it feels"
+    ]
+
+    [Character.where(active: 'Active').order('RANDOM()').first.name,
+     journal_entry_examples.sample]
+  end
+end

+ 37 - 9
app/commands/member.rb

@@ -7,6 +7,7 @@ class MemberCommand < BaseCommand
       nav: {
         all: [ Emoji::EYES, "View all info about the character" ],
         image: [ Emoji::PICTURE, "Scroll though the character's images" ],
+        journal: [ Emoji::NOTEBOOK, "Scroll though pages of journal entries" ],
         bags: [ Emoji::BAGS, "View the character's inventory" ],
         family: [ Emoji::FAMILY, "View related characters" ],
         user: [ Emoji::BUST, "View the writer's other characters in a list" ]
@@ -20,8 +21,8 @@ class MemberCommand < BaseCommand
         "Skips to the specified section, some options include: bio, type, status, " +
         "rumors, image, bags. If no section is given, R0ry will default to history",
         keyword:
-        "Displays a specific image, searched by its title, or keyword. " +
-        "Can only be used if the section option is `image`",
+        "Displays a specific image or journal, searched by its title, or keyword. " +
+        "Can only be used if the section option is `image` or `journal`",
       }
     }
   end
@@ -114,11 +115,25 @@ class MemberCommand < BaseCommand
      end
     end
 
-    # Find image if specified
-    image = CharImage.where(char_id: character.id).
-      find_by('keyword ilike ?', keyword || 'Default')
+    case section
+    when /image/i
+      # Find image if specified
+      image = CharImage.where(char_id: character.id).
+        find_by('keyword ilike ?', keyword || 'Default')
 
-    raise 'Image not found!' if keyword && !image
+      raise 'Image not found!' if keyword && !image
+    when /journal/i
+      # Find journal if specified
+      if keyword
+        journal = JournalEntry.where(char_id: character.id).
+          find_by('title ilike ?', keyword)
+
+        raise 'Journal not found!' if !journal
+      else
+        # Fetch Journal list if no keyword
+        journal = JournalEntry.where(char_id: character.id).take(10)
+      end
+    end
 
     # Ensure the content is appropriate for the current channel
     if sfw && ( image&.category == 'NSFW' || character.rating == 'NSFW' )
@@ -130,7 +145,8 @@ class MemberCommand < BaseCommand
       character: character,
       event: event,
       section: section,
-      image: image
+      image: image,
+      journal: journal
     )
 
     # Determine Carousel Type and create reply
@@ -140,6 +156,15 @@ class MemberCommand < BaseCommand
         carousel: image,
         reactions: ImageCarousel.sections.map{ |k,v| k }.push(Emoji::CROSS)
       )
+
+    elsif section&.match(/journal/i)
+      journal = journal.first unless journal.is_a? JournalEntry
+
+      BotResponse.new(
+        embed: embed,
+        carousel: journal,
+        reactions: JournalCarousel.sections.map{ |k,v| k }.push(Emoji::CROSS)
+      )
     else
       BotResponse.new(
         embed: embed,
@@ -150,7 +175,7 @@ class MemberCommand < BaseCommand
   end
 
   def self.example_command(event=nil)
-    sections = ['all', 'bio', 'type', 'status', 'rumors', 'image', 'bags']
+    sections = ['all', 'bio', 'type', 'status', 'rumors', 'image', 'bags', 'journal']
 
     case ['', 'user', 'name', 'section', 'keyword'].sample
     when ''
@@ -166,7 +191,10 @@ class MemberCommand < BaseCommand
        sections.sample]
     when 'keyword'
       i = CharImage.where.not(keyword: 'Default').order('RANDOM()').first
-      [Character.find(i.char_id).name, 'image', i.keyword]
+      j = JournalEntry.order('RANDOM()').first
+
+      [[Character.find(i.char_id).name, 'image', i.keyword],
+       [Character.find(j.char_id).name, 'journal', j.title || j.date]].sample
     end
   end
 end

+ 21 - 0
app/controllers/journal_controller.rb

@@ -0,0 +1,21 @@
+class JournalController
+  def self.journal_scroll(char_id:, page:, dir:)
+    total_journals = JournalEntry.where(char_id: char_id).length
+
+    new_page = case dir
+               when :left
+                 page <= 1 ? total_journals / 10 + 1 : page - 1
+               when :right
+                 page >= total_journals / 10 + 1 ? 1 : page + 1
+               else
+                 1
+               end
+
+    new_page
+  end
+
+  def self.fetch_page(char_id, page)
+    JournalEntry.where(char_id: char_id).
+      slice(page*10 - 10 .. page*10-1)
+  end
+end

+ 18 - 1
app/embeds/character.rb

@@ -1,4 +1,4 @@
-def character_embed(character:, event:, section: nil, image: nil)
+def character_embed(character:, event:, section: nil, image: nil, journal: nil)
   # Find the author, if they're a member, or in DMs use the event's author
   if event.server
     member = character.user_id.match(/public/i) ? 'Public' :
@@ -56,6 +56,8 @@ def character_embed(character:, event:, section: nil, image: nil)
     embed.thumbnail = nil
   when /bags?/i, /inventory/i
     fields = char_inv(character, fields)
+  when /journal/i
+    fields = char_journal(character, fields, journal)
   end
 
   # Add fields to embed
@@ -187,6 +189,21 @@ def char_inv(char, fields)
   fields
 end
 
+def char_journal(char, fields, journal)
+  if journal.is_a? JournalEntry
+    fields.push({ name: journal.title, value: journal.entry })
+  elsif journal.empty?
+    fields.push({ name: 'Error', value: 'No journal entries found' })
+  else
+    # Display each journal entry
+    journal.each do |j|
+      fields.push({ name: j&.title || j.date, value: j.entry })
+    end
+  end
+
+  fields
+end
+
 def char_dm_notes(char, fields, event)
   return fields unless ENV['DM_CH'].include?(event.channel.id.to_s)
 

+ 2 - 0
app/models/bot_response.rb

@@ -43,6 +43,8 @@ class BotResponse
       Carousel.create(message_id: message.id, landmark_id: @carousel.id)
     when Fable
       Carousel.create(message_id: message.id, fable_id: @carousel.id)
+    when JournalEntry
+      Carousel.create(message_id: message.id, char_id: @carousel.char_id, journal_page: 1)
     when CharImage
       Carousel.create(
         message_id: message.id,

+ 3 - 0
app/models/carousels.rb

@@ -6,6 +6,9 @@ class Carousel < ActiveRecord::Base
     if options
       # User List
       UserCarousel.update_embed(event, self)
+    elsif journal_page
+      # Journal
+      JournalCarousel.update_embed(event, self)
     elsif char_id
       # Character
       CharacterCarousel.update_embed(event, self)

+ 5 - 0
app/models/journal_entry.rb

@@ -0,0 +1,5 @@
+class JournalEntry < ActiveRecord::Base
+  validates :char_id, presence: true
+  validates :date, presence: true
+  validates :entry, presence: true
+end

+ 1 - 1
lib/emoji.rb

@@ -94,7 +94,7 @@ module Emoji
 
   CHAR_APP = [SPEECH_BUBBLE, PICTURE, BOOKS, NOTE, QUESTION, PEOPLE, WIZARD]
   IMG_APP = [DOG, KEY, FLAG, PAGE, BOOKS, VULGAR]
-  MEMBER = [EYES, PICTURE, BAGS, FAMILY, BUST, CROSS]
+  MEMBER = [EYES, PICTURE, NOTEBOOK, BAGS, FAMILY, BUST, CROSS]
   LANDMARK = [BOOKS, SKULL, MAP, HOUSES, PEOPLE, CROSS]
 
   APPLICATION = [Y, N, CRAYON, CROSS]