BraveNetwork / LinBot

Open source, general-purpose Discord chat bot written in C#
https://bravenetwork.github.io/
MIT License
2 stars 0 forks source link

Add an Auction feature #5

Open 23nd opened 6 years ago

23nd commented 6 years ago

(Navi) Create a new shop item type in LinBot called "auction." This item expires a certain amount of hours after being added to the shop, at which point it either disappears (if no one buys it) or goes to the highest bidder.

Every time someone "buys" this item, LinBot DMs them a confirmation that they have bid and sends a message in the channel stating something like "User has bid [quantity] [currency plural]. [item] now costs [quantity+X] [currency plural]." but does not give them an item. The gems are deducted from their total and the shop price goes up by X (the interval can be specified when adding the item).

At the end of the auction, the highest bidder gets the item and everyone else gets their gems refunded.

Syntax: .buy [item's shop index] [bid] - fails if the target is not an auction type item or the bid is lower than the current price
.buy [item shop index] - shows the time remaining on the auction for the item (buys the item if it is not an auction type item)

New syntax for .shopadd
.shopadd auction [starting price] [item name] [bid interval] [auction duration in hours]

Bonus: LinBot forces a cooldown so that people can't bid sooner than 5 seconds after someone else bid. This lets people see the new price of the item before bidding. Using .shop should show the current price of this item, and once the timer is finished it should disappear from the shop.

.h .buy
.buy
Buys an item from the shop on a given index. If buying items, make sure that the bot can DM you.
Usage
.buy 2
Module: Gambling

.h .shopadd

.shopadd
Adds an item to the shop by specifying type price and name. Available types are role and list. Requires Administrator server permission.
Usage
.shopadd role 1000 Rich
Module: Gambling

(Katsu) how should interrupted service be handled? such as when the bot dies before an auction completes, or connection is lost before auction completes

(Navi) Uh, from a technical standpoint I'm not sure, but in the end I think she should continue the auction where she left off. And keep the auction based on an internal timer, I don't think auction time should be lost due to downtime on the bot

(Katsu) okay i think that makes sense and is probably the simplest way to handle it even better if the project already has a way of restoring timers on reboot

(Navi) I'm not sure if it does. It might just reset them. The closest thing I could suggest looking at is .repeat

.repeat
Repeat a message every X minutes in the current channel. You can instead specify time of day for the message to be repeated at daily (make sure you've set your server's timezone). You can have up to 5 repeating messages on the server in total. Requires ManageMessages server permission.
Usage
.repeat 5 Hello there or .repeat 17:30 tea time
Module: Utility

(Navi) I know restarting the bot does... weird things to the messages that are on interval based repeats but I'm not sure if it resets the timer or restores it

For our purposes it's not a high priority. If you think it can be done fairly simply then we can include that but if you think it's going to require a lot of custom code to restore timers then don't worry about it.

23nd commented 6 years ago

@Navi-King While designing the tech spec, I came up with 7-9 more questions:

  1. Should also we allow a user to specify a specific date/time that the auction should end, or should we only accept intervals of time?
  2. When an auction ends, should we also DM the author to let them know who (if anyone) won the auction? Should we post auction results somewhere?
  3. Should the auction author make profit off of the bids?
  4. Can users update their bids?
  5. What types of items can be auctioned? Just text?
  6. What should happen if the bot disconnects from Discord while processing the winner of an auction? We can easily handle interrupted timers by calculating the time between the dateLastBid and dateStart/dateEnd, but when a timer ends there is a chance of disconnecting from Discord while trying to process a winner.
  7. Should we make Auction a separate feature from Shop?
    1. Editing Shop would require changes to 5 existing methods and adding 2 more auction-only columns to the ShopEntry table
    2. Creating a separate Auction feature would require more database changes, but will also be safe from any changes made to Nadeko's Shop feature. We'd also be able to better support more specialized types of auctions in the future.
    3. Either way we will still need
      1. Two new classes for managing Auction timers (based on MessageRepeater classes)
      2. Either making a User-Auction Relationship table or querying CurrencyTransactions in order to keep track of the latest bids
Navi-King commented 6 years ago
  1. Provided that using .shop shows the time remaining on the auction item, I think intervals of time are fine (maybe borrow the current time system used for reminders, etc.)
  2. I think that the auction should be bound to the channel the command to add the item to the shop is issued in, similar to how LinBot will output her music status to the channel from which you've queued the first song.
  3. The auction author should make a profit only off of the winning bid equivalent to whatever the shop profit is (80% or 90% I think)
  4. Users cannot update their bids if they are the most recent bid, but they can bid again if they've been outbid (e.g., Person A bids, Person B outbids Person A, Person A can now bid again)
  5. I was only thinking of text items to be auctioned, but roles could conceivably be auctioned as well. If it's not that much more work to add both, both would be good.
  6. Well, it depends on how you're planning to recall the most recent bid if the bot needs to restart. I would imagine using the DB to store the information relevant to the item being bid (server/type/name/channel of auction/start time/end time/time remaining) and to the current winner (user ID of highest bid, highest bid value) as well as auction status (1 for in progress, 2 for completed, and maybe 0 for cancelled i.e., the owner removes the auction item from the shop before the auction completes). If the auction status is 1 and the time remaining is 0, then LinBot will attempt to award the item and upon successfully awarding the item, set the auction status to 2. Or maybe there's a less complicated way to do it.
  7. I'd like auction to be a part of the shop, but compatibility is key so I think creating a separate auction feature is going to be more stable in the long run (I also imagine modifying the DB is simpler than modifying the source code). Maybe created .auctionhouse, .auctionadd, etc. parallel to the shop commands and have it mimic shop functionality using auction methodology.
23nd commented 6 years ago

Okay, if auctions are going to be bound to the channel that they were defined in, that raises a few more questions:

  1. Should there be a Channel mention/ID parameter for the .AuctionAdd command? Without this, you won't be able to define auctions in a private channel for public use.
  2. Should the auction's home channel be notified when an auction is created? Should it also be notified when the auction is cancelled?
  3. Should .AuctionHouse only list auctions for the current channel? Or should it just specify which channel each auction is active in? Or maybe it should accept a Channel mention/ID parameter to filter the results with?

I also have some other questions:

  1. Should cooldowns be managed by the .cmdcooldown command? Or do you prefer it to be managed internally with a AuctionCooldown config setting?
  2. Can bids be submitted anonymously?
  3. Regarding recovery from a disconnect: I could add a RecoverAuction task to the NadekoBot client.Ready event handler, but at this time I am unsure how complicated this would be to get right. Alternatively (or in addition), we could add the following commands:
    1. .AuctionInfo to check the status and get the bid history for a given auction item. With this, people can check if the auction failed to deliver the reward to the winner.
    2. .AuctionClaim for winners to claim their reward should an auction fail to deliver (whether due to a disconnect or some other error like being blocked from DMs or a Role not existing).
Navi-King commented 6 years ago
  1. There doesn't need to be a channel mention/ID parameter for auction add. The confirmation message of the successful addition of the auction item can serve as the notification that the auction has been created.
  2. There should be a notification when the auction is cancelled
  3. .Auctionhouse should specify which items are in which channels. I don't think they need to be filtered, you can just paginate the results similar to how .shop is paginated.
  4. If we want to be thorough, we should probably have a separate config setting because if there are multiple auctions occurring simultaneously .cmdcooldown would interfere with bidding from one to the next.
  5. No, bids cannot be submitted anonymously.
  6. I think the commands will be suitable. The way you were talking before made it sound like you could restore the auction if it was interrupted by a bot crash or something and the only issues would be if it crashed while awarding the auction item or failed to award the auction item for a permissions reason. I think those cases will be minimal so I think the commands will provide enough debugging info/resolution to make things right.
23nd commented 6 years ago

Re: 1. If auctions will always be defined in their home channel, then you won't be able to privately define the reward for the auction (like a steam code) unless there is a command for changing the reward value before it gets delivered.

Navi-King commented 6 years ago

oh I see. Hmm, in that guess yeah I guess it would be good to have a channel argument.

23nd commented 6 years ago

Oh, upon closer inspection, I see that .shoplistadd is what you currently use to add secret rewards to an existing item in the shop. Would you prefer something like that for auctions?

This would mean:

  1. Channel would no longer be needed as a parameter for .shopadd
  2. A single auction could have multiple rewards (and multiple winners)
    1. Winners get rewarded prizes in order of their bid rank
    2. Auctions would need to keep track of how many prizes there are and only refund those below the Nth prize bid rank
    3. Leftover prizes would either be cleared at the end of an auction, given to the top bidder, or saved for the next auction (I'm leaning toward the latter)
  3. A single auction could be renewable
    1. When an auction ends, you can add more items
    2. When an auction ends, you can reset the timer (if there are items remaining or recently added)
    3. When initially creating an auction you would run 3 commands: .auctionadd, .auctionaddprize, and .auctionstart
  4. .auctioninfo would need to show the results separately for each iteration, with the latest iteration being displayed by default
Navi-King commented 6 years ago
  1. Is it possible to determine a first/second/etc. prize? I don't think the current implementation of lists supports that (IIRC it's random)
  2. For auction info, would each iteration get its own ID? Is there a way to list all the iterations associated with a particular auction list? It sounds like this is moving more towards the list version of things which is potentially fine but it sounds like it could potentially become complicated to create.
23nd commented 6 years ago
  1. The way I was going to handle it was to iterate over the prize list (in order) so that the Nth prize goes to the Nth highest bidder. However, it may get complicated if you want to be able to change the order of rewards after you've already added them. In such a case, it might be best to let you remove items and add them back in the order you want them (1st to the top bidder, 2nd to the next, etc.).

  2. Yes, each iteration has its own ID. You can get information about a specific iteration by doing .auctioninfo AuctionID ITER=IterationID (Page#) so that IterationID and Page# do not get mixed up. I was planning to have the default .auctioninfo AuctionID show info for the latest iteration, but I can easily change this to list all of the existing iterations with their Start/End dates and status.

I added the quotes for these to the commission sheet so you can see how long/how much this will take. The tech spec is nearly done as well-- just fine-tuning the details for each command right now. I also just added the tech spec folder to your LinBot folder.

Navi-King commented 6 years ago

I think both of your suggestions are good. I'd also like to suggest that the output for the complete .auctioninfo command show how many pages there are kind of like in the other pagination (e.g., page 1/X).

23nd commented 6 years ago

Yes, of course!