Skip to content

Instantly share code, notes, and snippets.

@AL1L
Last active April 23, 2020 23:14
Show Gist options
  • Save AL1L/f25f8654935708eaeb6677ff17443735 to your computer and use it in GitHub Desktop.
Save AL1L/f25f8654935708eaeb6677ff17443735 to your computer and use it in GitHub Desktop.

A flow is a way to get user information without the hastle of recoding every little piece Each object an action type, each type has their own requirements and options

Actions: (* = required)

  • reaction
    • Prompts the user to select a reaction
    • Options
      • *key: string - key to be used with placeholders (e.g. if key is desc you can do "Ticket Description: {desc}")
        • in addition to this, reaction will also have a key with -raw appended to it to get the raw reaction value
      • *reactions: array - The reactions to be used
        • There are some templates that return custom options
          • yes_no - returns boolean
          • 5_star - returns 1-5 (not implemented)
          • opinion - returns "green" "yellow" or "red" (not implemented)
          • thumbs - returns "up" or "down" (not implemented)
        • You can also set it as an object and the keys will be the emojis and values will set placeholder value to it
      • timeout: int - if the user doesnt respond in this many seconds the flow will timeout
      • on_timeout: string - Message to be sent if the flow times out
  • input
    • Asks the user for a message input
    • Options
      • *key: string - key to be used with placeholders
      • type: string - Default is str, accepted values are guild, role, channel, member, user, int, float, and str
      • max_length: int - Max length for the string to be, default 2048
      • timeout: int - if the user doesnt respond in this many seconds the flow will timeout
      • on_timeout: string - Message to be sent if the flow times out
  • set
    • Options
      • *key: string - key to be used with placeholders
      • *value: any - value of the placeholder
  • eval
    • Options
      • *key: string - key to be used with placeholders
      • *eval: string - Code to evaluate output will be set as placeholder. Ask Allen#0001 for more info
  • exec
    • Options
      • *exec: string - Code to execute. Ask Allen#0001 for more info
  • message
    • no action is taken

Each action may have these three additional options:

  • then: array, object - Flow items to be executed after the current one
    • if the value is an array, it will execute each flow item in order
    • if the value is a object then the key will be evaluated as an if statement and the action will only be executed if it is true
      • the key may also be else to run if no other action has been taken in the then
      • e.g The previous action set a key called category to a string, setting the key to be "category == 'Foo'" will check the value to be Foo
  • if: string - The flow action will only be executed if the statement is evaluated to try
    • e.g The previous action set a key called category to a string, setting the key to be "category == 'Foo'" will check the value to be Foo
  • embed: null, object
    • if omitted, nothing happens
    • if set to null or false, the flow message will be deleted
    • if set to a object then the options may be as listed, nothing is required, but nothing may be empty
      • title: string
      • description: string
      • fields: object, array
      • footer: string
      • color: int - Cyan is #00FFFF or 0x00FFFF in hex, or 65535 as an int
      • author_text: string
      • image: string - must be a correctly formatted url
      • thumbnail: string - must be a correctly formatted url
{
action: "reaction",
reactions: {
"1": "Vector Graphic Design",
"2": "Cinematics and Trailers",
"3": "Minecraft Builds",
"4": "Discord Bots and Server Automation",
},
key: "category",
embed: {
title: "Please select a category:",
description: "1. Graphic Design\n2. Trailers and Cinematics\n3. Minecraft Builds\n4. Discord Bots and Server Automation\n5. Middleman Service _(coming soon)_",
},
then: {
"category == 'Vector Graphic Design'": {
action: "reaction",
reactions: {
"1": "- Thread Design",
"2": "- Icons and Logos",
"3": "- Mascots",
"4": "- App Prototype",
"5": "- Web Prototype",
},
key: "subcategory",
embed: {
title: "Please select a subcategory:",
description: "- Thread Design\n- Icons and Logos\n- Mascots\n- App Prototype\n- Web Prototype",
},
},
"category == 'Cinematics and Trailers'": {
action: "reaction",
reactions: {
"1": "- Server Trailer",
"2": "- Build Cinematic",
},
key: "subcategory",
embed: {
title: "Please select a subcategory:",
description: "- Server Trailer\n- Build Cinematic",
},
},
"category == 'Discord Bots and Server Automation'": {
action: "set",
key: "subcategory",
value: "",
},
"category == 'Minecraft Builds'": {
action: "set",
key: "subcategory",
value: "",
},
"category == 'Discord Bots and Server Automation'": {
action: "set",
key: "subcategory",
value: "",
},
"category == 'Middleman Service'": {
action: "set",
key: "subcategory",
value: "",
},
},
},
{
action: "input",
max_length: 128,
key: "budget",
embed: {
description: "What is your budget? This is the max amount you are willing to spend, we likely wont ask for your max, but its important to know this.",
},
},
{
action: "input",
max_length: 1024,
key: "description",
embed: {
description: "Please exlpain in detail what you need help with? (Max 1024 characters)",
},
},
{
action: "message",
embed: {
title: "Thank you!",
description: "Please wait while someone gets back to you",
"inline": false,
fields: {
Category: "{category} {subcategory}",
Budget: "{budget}",
Description: "{description}",
},
},
}
async def user_flow_section(self, channel, user, section, msg, rtn, on_timeout=None, context={}):
if isinstance(section, (list, tuple)):
for t in section:
await self.user_flow_section(channel, user, t, msg, rtn, on_timeout=on_timeout, context=context)
return
elif not isinstance(section, dict):
return
def str_condition(s):
if s == "else":
return
return bool(eval(s, {
**rtn,
"msg": msg.msg
}))
if 'action' not in section:
ran = False
for c in section:
if str_condition(c):
ran = True
await self.user_flow_section(channel, user, section[c], msg, rtn, on_timeout=on_timeout, context=context)
if not ran and 'else' in section:
await self.user_flow_section(channel, user, section['else'], msg, rtn, on_timeout=on_timeout, context=context)
return
if 'if' in section:
if not str_condition(section['if']):
return
action = section['action'].lower()
if 'embed' in section:
if section['embed'] is None:
await msg.delete()
else:
embed = self.generate_embed(**section['embed'], format=rtn, author=user)
await msg.s(embed)
if action == 'reaction':
if msg.msg is None:
return
args = {}
if 'timeout' in section:
args['timeout'] = section['timeout']
templates = {
"yes_no": {
"✅": True,
"❌": False
}
}
template = None
reactions = section['reactions']
if isinstance(reactions, str):
if reactions in templates:
reactions = templates[reactions]
if isinstance(reactions, dict):
template = reactions
reactions = list(template.keys())
m, raw = await self.ask(None, choices=reactions, channel=channel, msg=msg.msg, user=user, **args)
del m
out = raw
if template is not None:
out = template[reactions[out]]
if 'key' in section and section['key']:
rtn[section['key']] = out
rtn[section['key'] + '-raw'] = raw
elif action == 'input':
async def on_invalid():
if isinstance(section['on_invalid'], str):
await msg.s(self.generate_embed(description=section['on_invalid'], title="Oh no!", color=discord.Colour.red(), author=user))
elif 'on_invalid' in section:
if callable(section['on_invalid']):
await section['on_invalid'](msg.msg)
args = {}
if 'type' in section:
args['expected_type'] = section['type']
if 'timeout' in section:
args['timeout'] = section['timeout']
if 'bot' in section:
args['bot'] = section['bot']
while True:
out = await self.input(channel=channel, user=user, guild=channel.guild, on_invalid=on_invalid, delete=True, **args)
if 'max_length' in section and isinstance(out, str):
max_len = section['max_length']
if len(out) > max_len:
continue
if 'check' in section:
if callable(section['check']):
if not await section['check'](out, msg):
continue
break
if 'key' in section and section['key']:
rtn[section['key']] = out
elif action == 'set':
if 'value' in section:
if 'key' in section and section['key']:
rtn[section['key']] = section['value']
elif action == 'eval':
if 'value' in section:
if 'key' in section and section['key']:
rtn[section['key']] = eval(section['eval'], {"rtn": rtn, **context})
elif action == 'exec':
exec(section['exec'], {"rtn": rtn, **context})
if 'then' in section:
await self.user_flow_section(channel, user, section['then'], msg, rtn, on_timeout=on_timeout, context=context)
async def user_flow(self, channel, user, template, on_timeout=None, return_msg=False, context={}):
is_dm = not isinstance(channel, discord.TextChannel)
rtn = {}
class Message():
def __init__(self):
self.msg = None
async def s(self, embed):
if is_dm:
await self.delete()
if self.msg is None:
self.msg = await channel.send(embed=embed)
else:
await self.msg.edit(embed=embed)
async def delete(self):
if self.msg is not None:
await self.msg.delete()
self.msg = None
msg = Message()
try:
for t in template:
await self.user_flow_section(channel, user, t, msg, rtn, on_timeout=on_timeout, context=context)
except TimeoutError:
if callable(on_timeout):
on_timeout()
if return_msg:
return rtn, msg.msg
return rtn
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment