рд╕реНрд▓реИрдХ рдХреЗ рд▓рд┐рдП рдПрдХреНрд╢рди-рд╕рдХреНрд╖рдо рдПрдкреНрд▓рд┐рдХреЗрд╢рди рд╡рд┐рдХрд╕рд┐рдд рдХрд░рдирд╛



рдЕрдиреБрд╡рд╛рджрдХ рд╕реЗ: рд╣рдо рдЖрдкрдХреЗ рд▓рд┐рдП Tomomi Imura рджреНрд╡рд╛рд░рд╛ рдПрдХ рд▓реЗрдЦ рдкреНрд░рдХрд╛рд╢рд┐рдд рдХрд░рддреЗ рд╣реИрдВ рдХрд┐ рдХреИрд╕реЗ рд╕реНрд▓реИрдХ рдХреЗ рд╕рд╛рде рдПрдХреАрдХреГрдд рдЕрдиреБрдкреНрд░рдпреЛрдЧ рд╡рд┐рдХрд╕рд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рдП

рдХрд╛рд░реНрд░рд╡рд╛рдИ рд╡рд╣ рд╣реИ рдЬреЛ рд╕реНрд▓реИрдХ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛рдУрдВ рдХреЛ рд╕рдВрджреЗрд╢реЛрдВ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЖрдкрдХреЗ рдЖрд╡реЗрджрди рдХреЗ рд╕рд╛рде рдмрд╛рддрдЪреАрдд рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддреА рд╣реИ: рдмрдЧ рдХреЗ рд╕рд╛рде рдПрдХ рд╕рдорд╕реНрдпрд╛ рдХреА рд░рд┐рдкреЛрд░реНрдЯ рдХрд░реЗрдВ, рд╣реЗрд▓реНрдкрдбреЗрд╕реНрдХ рдХреЛ рдЕрдиреБрд░реЛрдз рднреЗрдЬреЗрдВ рдпрд╛ рдХреБрдЫ рдФрд░ рдХрд░реЗрдВред рдпрд╣ рд╕реБрд╡рд┐рдзрд╛ рдЯрд┐рдкреНрдкрдгреА рдпрд╛ рд╕рд╛рдЭрд╛ рдХрд░рдиреЗ рдЬреИрд╕реА рдЕрдВрддрд░реНрдирд┐рд╣рд┐рдд рд╕реБрд╡рд┐рдзрд╛рдУрдВ рдХреЗ рд╕рдорд╛рди рд╣реИред

рдпрд╣ рдЯреНрдпреВрдЯреЛрд░рд┐рдпрд▓ рдЖрдкрдХреЛ рд▓рд╛рдЗрд╡ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдмрдирд╛рдиреЗ рдХреА рдкреВрд░реА рдкреНрд░рдХреНрд░рд┐рдпрд╛ рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рдмрддрд╛рдПрдЧрд╛ред

рд╕реНрдХрд┐рд▓рдмреЙрдХреНрд╕ рд╕рд▓рд╛рд╣ рджреЗрддрд╛ рд╣реИ: рджреЛ рд╕рд╛рд▓ рдХрд╛ рд╡реНрдпрд╛рд╡рд╣рд╛рд░рд┐рдХ рдХреЛрд░реНрд╕ "рдореИрдВ рдПрдХ рдкреНрд░реЛ рд╡реЗрдм рдбреЗрд╡рд▓рдкрд░ рд╣реВрдВ ред "

рд╣рдо рдЖрдкрдХреЛ рдпрд╛рдж рджрд┐рд▓рд╛рддреЗ рд╣реИрдВ: "рд╣реИрдмрд░" рдХреЗ рд╕рднреА рдкрд╛рдардХреЛрдВ рдХреЗ рд▓рд┐рдП - "рд╣реИрдмрд░" рдкреНрд░рдЪрд╛рд░рдХ рдХреЛрдб рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдХрд┐рд╕реА рднреА рд╕реНрдХрд┐рд▓рдмреЙрдХреНрд╕ рдХреЛрд░реНрд╕ рдХреЗ рд▓рд┐рдП рдкрдВрдЬреАрдХрд░рдг рдХрд░рддреЗ рд╕рдордп 10,000 рд░реВрдмрд▓ рдХреА рдЫреВрдЯред

рдЯреНрдпреВрдЯреЛрд░рд┐рдпрд▓ рдЙрди рд▓реЛрдЧреЛрдВ рдХреЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧреА рд╣реЛрдЧрд╛ рдЬреЛ рд╕реНрд▓реИрдХ рдПрдкреАрдЖрдИ рд╕реАрдЦрдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВред рдПрдкреНрд▓рд┐рдХреЗрд╢рди рд╡рд┐рдХрд╕рд┐рдд рдХрд░рддреЗ рд╕рдордп, Node.js рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдпрджрд┐ рдЖрдк рджреЛрд╣рд░рд╛рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ рдХрд┐ рдмрд╛рддрдЪреАрдд рдХрд┐рд╕ рдмрд╛рд░реЗ рдореЗрдВ рд╣реИ, рддреЛ рдЗрд╕реЗ рд╕реНрдерд╛рдкрд┐рдд рдХрд░реЗрдВред

рд╕рдорд╛рдкреНрдд рдкрд░рд┐рдпреЛрдЬрдирд╛ рдЧрд┐рдЯрд╣рдм рдкрд░ рдорд┐рд▓ рд╕рдХрддреА рд╣реИ , рдЧреНрд▓рд┐рдЪ рдкрд░ рд╕рд░рд▓реАрдХреГрдд рд╕рдВрд╕реНрдХрд░рдг рдХреЗ рд╕рд╛рдеред

ClipIt! рд╕реБрд╕реНрдд рдХреЗ рд▓рд┐рдП

рд╣рдо рдХрд╛рд▓реНрдкрдирд┐рдХ ClipIt рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЗ рд▓рд┐рдП рд╕реНрд▓реИрдХ-рдРрдк рд╡рд┐рдХрд╕рд┐рдд рдХрд░реЗрдВрдЧреЗ! рдЖрдЗрдП рдХрд▓реНрдкрдирд╛ рдХрд░реЗрдВ рдХрд┐ рд╣рдо рдПрдХ рд╡реЗрдм рд╕реЗрд╡рд╛ рдХрд╛ рдкреНрд░рдмрдВрдзрди рдХрд░ рд░рд╣реЗ рд╣реИрдВ рдЬреЛ рдЖрдкрдХреЛ рдПрдХ рд╡реЗрдм рдкреЗрдЬ рдХреЗ "рд▓реЗрдЯ" рднрд╛рдЧ рдХреЛ рдбреЗрдЯрд╛рдмреЗрд╕ рдореЗрдВ рд╕рдВрдЧреНрд░рд╣реАрдд рдХрд░рдХреЗ рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИред рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдореЛрдмрд╛рдЗрд▓ рдбрд┐рд╡рд╛рдЗрд╕ рдпрд╛ рдкреАрд╕реА рд╕реЗ рд╕рдВрдЧреНрд░рд╣реАрдд рд╕рд╛рдордЧреНрд░реА рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд╕рдХрддреЗ рд╣реИрдВ, рд╕реЗрд╡рд╛ рдмрд╣реБ-рдкреНрд▓реЗрдЯрдлрд╝реЙрд░реНрдо рд╣реИред рдФрд░ рдпрд╣ рднреА рдХреНрд▓рд┐рдк! рдЖрдкрдХреЛ рдХрд╛рд░реНрд░рд╡рд╛рдИ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдПрдХ рд╕реБрд╕реНрдд рд╕рдВрджреЗрд╢ рд╡рд╛рддрд╛рд╡рд░рдг рдореЗрдВ рдкрд╛рда рд╕рдВрд▓рдЧреНрди рдХрд░рдиреЗ рдХреА рдЕрдиреБрдорддрд┐ рджреЗрддрд╛ рд╣реИред

рдпрд╣рд╛рдБ рдпрд╣ рдкреНрд░рджрд░реНрд╢рд┐рдд рдХрд░рддрд╛ рд╣реИ рдХрд┐ рдЕрдиреБрдкреНрд░рдпреЛрдЧ рдХреИрд╕реЗ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИред



рдФрд░ рдпрд╣рд╛рдВ ClipIt рдХреЗ рд╕рд╛рде рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдЗрдВрдЯрд░реИрдХреНрд╢рди рдПрд▓реНрдЧреЛрд░рд┐рджрдо рд╣реИ! рд╕реБрд╕реНрдд рд╕реЗ:

  • рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рд╕рдВрджреЗрд╢ рдкрд░ рдордВрдбрд░рд╛рддрд╛ рд╣реИ рдФрд░ рдбреНрд░реЙрдк-рдбрд╛рдЙрди рдореЗрдиреВ рдореЗрдВ рдХреНрд▓рд┐рдк рд╕рдВрджреЗрд╢ рдХрд╛ рдЪрдпрди рдХрд░рддрд╛ рд╣реИред
  • рдПрдХ рд╕рдВрд╡рд╛рдж рдЦреБрд▓рддрд╛ рд╣реИ, рдЬреЛ рдЕрдзрд┐рдХ рдЕрд╡рд╕рд░ рджреЗрддрд╛ рд╣реИред
  • рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХрд╛рд░реНрд░рд╡рд╛рдИ рдХреА рдкреБрд╖реНрдЯрд┐ рдХрд░рддрд╛ рд╣реИред
  • ClipIt! Slack рдХреЗ рд▓рд┐рдП рдХреНрд▓рд┐рдк рдЗрдЯ рдХреЛ рд╕рдВрджреЗрд╢ рдирд┐рд░реНрдпрд╛рдд рдХрд░рддрд╛ рд╣реИ! рдбреЗрдЯрд╛рдмреЗрд╕ред
  • ClipIt! рд╕реБрд╕реНрдд рдХреЗ рд▓рд┐рдП рдкреБрд╖реНрдЯрд┐ рдХреЗ рд╕рд╛рде рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЛ рдПрдХ рдбреАрдПрдо рднреЗрдЬрддрд╛ рд╣реИред

рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЛ рдХрд╕реНрдЯрдорд╛рдЗрдЬрд╝ рдХрд░реЗрдВ


рдЕрдкрдиреЗ рд╕реНрд▓реИрдХ рдЦрд╛рддреЗ рдореЗрдВ рдкреНрд░рд╡реЗрд╢ рдХрд░реЗрдВ рдФрд░ рдЗрд╕ рд▓рд┐рдВрдХ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдмрдирд╛рдПрдВред рдЙрд╕рдХрд╛ рдирд╛рдо рдФрд░ рджрд╛рдпрд░рд╛ рджрд░реНрдЬ рдХрд░реЗрдВред



рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдмрдирд╛рдПрдВ рдмрдЯрди рдкрд░ рдХреНрд▓рд┐рдХ рдХрд░реЗрдВ, рдлрд┐рд░ рдореВрд▓ рдЬрд╛рдирдХрд╛рд░реА, рдРрдк рдХреНрд░реЗрдбреЗрдВрд╢рд┐рдпрд▓реНрд╕ рдкрд░ рд╕реНрдХреНрд░реЙрд▓ рдХрд░реЗрдВред



рдЙрд╕рдХреЗ рдмрд╛рдж, рд╕рд╛рдЗрдирд┐рдВрдЧ рд╕реАрдХреНрд░реЗрдЯ рдЦреЛрд▓реЗрдВ рдФрд░ рдХреЛрдб рдХреЛ рдХреЙрдкреА рдХрд░рдХреЗ рдПрдирд╡рд╛рдпрд░рдирдореЗрдВрдЯ рд╡реЗрд░рд┐рдПрдмрд▓ SLACK_SIGNING_SECRET рдХреЗ рд░реВрдк рдореЗрдВ рд░реВрдЯ рдиреЛрдб рдореЗрдВ рдЕрдкрдиреА .env рдлрд╛рдЗрд▓ рдореЗрдВ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВред рдореИрдВ "рд░рд┐рдХреНрд╡реЗрд╕реНрдЯ рд╡реЗрд░рд┐рдлрд┐рдХреЗрд╢рди" рд╕реЗрдХреНрд╢рди рдореЗрдВ, рдпрд╣ рд╕рдм рдХреНрдпрд╛ рд╣реИ рдФрд░ рдЗрд╕реЗ рдХреИрд╕реЗ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд░рдирд╛ рд╣реИ, рдереЛрдбрд╝рд╛ рдХрдо рдмрддрд╛рдКрдВрдЧрд╛ред

SLACK_SIGNING_SECRET = 15770a ...

рдЕрдкрдиреЗ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдЖрдЗрдХрди рдФрд░ рд╡рд┐рд╡рд░рдг рдХреЗ рд╕рд╛рде рдкреНрд░рджрд░реНрд╢рди рдЬрд╛рдирдХрд╛рд░реА рднрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдереЛрдбрд╝рд╛ рдФрд░ рдиреАрдЪреЗ рд╕реНрдХреНрд░реЙрд▓ рдХрд░реЗрдВред

рдЕрдм рдЗрдВрдЯрд░рдПрдХреНрдЯрд┐рд╡ рдЕрд╡рдпрд╡реЛрдВ рдореЗрдВ рдЕрдиреНрддрд░рдХреНрд░рд┐рдпрд╛рд╢реАрд▓рддрд╛ рдХреЛ рд╕рдХреНрд╖рдо рдХрд░реЗрдВред рдРрд╕рд╛ рдХрд░рдиреЗ рдкрд░, рдЖрдкрдХреЛ рдкреГрд╖реНрда рдкрд░ рдЕрдзрд┐рдХ рдлрд╝реАрд▓реНрдб рджрд┐рдЦрд╛рдИ рджреЗрдВрдЧреЗред



рдпрд╣ рдЕрдиреБрд░реЛрдз URL рджрд░реНрдЬ рдХрд░рдиреЗ рдХрд╛ рд╕рдордп рд╣реИ - рдпрд╣ рд╡рд╣ рдкрддрд╛ рд╣реИ рдЬрд┐рд╕рдХреЗ рд▓рд┐рдП рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рджреНрд╡рд╛рд░рд╛ рдХрд╛рд░реНрд░рд╡рд╛рдИ рд╢реБрд░реВ рдХрд░рдиреЗ рдкрд░ рд╕реНрд▓реИрдХ рд╕рдВрдмрдВрдзрд┐рдд рдбреЗрдЯрд╛ рднреЗрдЬрддрд╛ рд╣реИред

рдпрд╣ рдЖрдкрдХреЗ рд╕рд░реНрд╡рд░ рдХрд╛ URL рд╣реЛрдирд╛ рдЪрд╛рд╣рд┐рдП рдЬрд╣рд╛рдВ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЛрдб рдЪрд▓ рд░рд╣рд╛ рд╣реИред рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, рдпрджрд┐ рдЖрдкрдиреЗ рдЧреНрд▓рд┐рдЪ рдореЗрдВ рдпрд╣ рд╕рдм рдкреЛрд╕реНрдЯ рдХрд┐рдпрд╛ рд╣реИ, рддреЛ рдЖрдкрдХрд╛ URL рдХреБрдЫ рдРрд╕рд╛ рджрд┐рдЦреЗрдЧрд╛ рдЬреИрд╕реЗ example.glitch.me/actions ред рдпрджрд┐ рдЖрдк ngrok рдЬреИрд╕реА рд╕реЗрд╡рд╛рдУрдВ рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рддреЗ рд╕рдордп рд╕реБрд░рдВрдЧ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ, рддреЛ рд╕реЗрд╡рд╛ URL рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░реЗрдВ (рдЙрджрд╛рд╣рд░рдг рдХреЗ рд▓рд┐рдП, example.ngrok.io , рдФрд░ рдлрд┐рд░ / рдХреНрд░рд┐рдпрд╛рдПрдБ рдЬреЛрдбрд╝реЗрдВ)ред

рдПрдХ рдмрд╛рд░ рдЬрдм рдЖрдк рдЕрдиреБрд░реЛрдз URL рджрд░реНрдЬ рдХрд░ рд▓реЗрддреЗ рд╣реИрдВ, рддреЛ рдХрд╛рд░реНрд░рд╡рд╛рдИ рдХреЗ рд▓рд┐рдП рдиреАрдЪреЗ рд╕реНрдХреНрд░реЙрд▓ рдХрд░реЗрдВ рдФрд░ рдирдИ рдХрд╛рд░реНрд░рд╡рд╛рдИ рдмрдирд╛рдПрдБ рдкрд░ рдХреНрд▓рд┐рдХ рдХрд░реЗрдВред рдлрд╝реЙрд░реНрдо рднрд░реЗрдВ:



рдмрдирд╛рдПрдБ рдкрд░ рдХреНрд▓рд┐рдХ рдХрд░реЗрдВ, рдлрд┐рд░ рдкрд░рд┐рд╡рд░реНрддрди рд╕рд╣реЗрдЬреЗрдВред

рдЗрд╕рдХреЗ рдмрд╛рдж, рдмреЙрдЯ рдпреВрдЬрд░реНрд╕ рдХреЗ рдкрд╛рд╕ рдЬрд╛рдПрдВред рдПрдХ рдмреАрдУрдЯреА рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдЬреЛрдбрд╝реЗрдВ рдкрд░ рдХреНрд▓рд┐рдХ рдХрд░реЗрдВ рдФрд░ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдмреЙрдЯ рдХрд╛ рдирд╛рдо рджреЗрдВред



рдЕрдм Add Bot User рдкрд░ рдХреНрд▓рд┐рдХ рдХрд░реЗрдВ рдФрд░ рд╕реЗрд╡ рдХрд░реЗрдВред

рдЕрдЧрд▓рд╛ рдХрджрдо OAuth & рдЕрдиреБрдорддрд┐рдпрд╛рдБ рдкрд░ рдЬрд╛рдПрдВ рдФрд░ рдЗрдВрд╕реНрдЯреЙрд▓ рдРрдк рдХреЛ рдХрд╛рд░реНрдпрд╕реНрдерд╛рди рдкрд░ рдХреНрд▓рд┐рдХ рдХрд░реЗрдВред рд╕реНрдерд╛рдкрдирд╛ рдкреВрд░реНрдг рд╣реЛрдиреЗ рдХреЗ рдмрд╛рдж, рдкреГрд╖реНрда рдЖрдкрдХреЛ рдЯреЛрдХрди рдПрдХреНрд╕реЗрд╕ рдХреЗ рд╕рд╛рде OAuth рдФрд░ рдЕрдиреБрдорддрд┐ рдкрд░ рд╡рд╛рдкрд╕ рдХрд░ рджреЗрдЧрд╛ред рдмреЙрдЯ рдЯреЛрдХрди рдХреЛ рдХреЙрдкреА рдХрд░реЗрдВ рдФрд░ рдЗрд╕реЗ .env рдлрд╛рдЗрд▓ рдореЗрдВ рд╕реЗрд╡ рдХрд░реЗрдВред

SLACK_ACCESS_TOKEN = xoxb-214 ...

рдЗрд╕рдХреЗ рдЕрд▓рд╛рд╡рд╛, рд╕реНрдХреЛрдкреНрд╕ рдХреЛ рдЙрд╕реА рдкреЗрдЬ рдкрд░ рд╕рдХреНрд░рд┐рдп рдХрд░рдирд╛ рд╣реЛрдЧрд╛ред рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рдмрд╛рдж, рд╕реБрдирд┐рд╢реНрдЪрд┐рдд рдХрд░реЗрдВ рдХрд┐ рдмреЙрдЯ рдФрд░ рдХрдорд╛рдВрдб рджреЛрдиреЛрдВ рд╣рд╛рдЗрд▓рд╛рдЗрдЯ рдХрд┐рдП рдЧрдП рд╣реИрдВред

рдЕрдм рдЬрдм рд╕рднреА рд╕реЗрдЯрд┐рдВрдЧреНрд╕ рддреИрдпрд╛рд░ рд╣реИрдВ, рд╣рдо рдмрдирд╛рдирд╛ рд╢реБрд░реВ рдХрд░рддреЗ рд╣реИрдВ - рд╣рдо рдПрдХ рдЖрд╡реЗрджрди рд▓рд┐рдЦ рд░рд╣реЗ рд╣реИрдВред

рдЕрдиреБрдкреНрд░рдпреЛрдЧ рдирд┐рд░реНрдорд╛рдг


рдЬреИрд╕рд╛ рдХрд┐ рдКрдкрд░ рдмрддрд╛рдпрд╛ рдЧрдпрд╛ рд╣реИ, рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдмрдирд╛рддреЗ рд╕рдордп, Node.js рдФрд░ ExpressJS рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред рдЗрд╕ рд╕рдм рдХреЗ рд╕рд╛рде рдХрд╛рдо рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рдПрдХреНрд╕рдкреНрд░реЗрд╕рдЬреЗрдПрд╕ рдирд┐рд░реНрднрд░рддрд╛, рдмреЙрдбреАрдкреЗрдпрд░ рдФрд░ рдХреБрдЫ рдФрд░ рд╕реНрдерд╛рдкрд┐рдд рдХрд░реЗрдВред рддреЛ, рдореИрдВ qs рдХреЗ рд╕рд╛рде axios HTTP рдЕрдиреБрд░реЛрдз рдХреНрд▓рд╛рдЗрдВрдЯ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддрд╛ рд╣реВрдВред

$ npm рд╕реНрдерд╛рдкрд┐рдд рдПрдХреНрд╕рдкреНрд░реЗрд╕ рдмреЙрдбреА-рдкрд╛рд░реНрд╕рд░ axios qs dotenv --save

рд╕рдмрд╕реЗ рдорд╣рддреНрд╡рдкреВрд░реНрдг рдХреЗ рд╕рд╛рде рд╢реБрд░реВ рдХрд░рддреЗ рд╣реИрдВред рдЬрдм рд╣рдо рдЕрдзрд┐рдХ рд╕реБрд╡рд┐рдзрд╛рдПрдБ рдЬреЛрдбрд╝реЗрдВрдЧреЗ рддреЛ рд╣рдо рдмрд╛рдж рдореЗрдВ рдХреЛрдб рдмрджрд▓ рджреЗрдВрдЧреЗред рд╕рдмрд╕реЗ рдкрд╣рд▓реЗ, index.js рдлрд╝рд╛рдЗрд▓ рдмрдирд╛рдПрдБред рдЗрд╕ рдлрд╝рд╛рдЗрд▓ рдореЗрдВ, рд╣рдо рд╕рдВрдмрдВрдзрд┐рдд рдкреЛрд░реНрдЯ рдкрд░ рд╕реБрдирдиреЗ рдХреЗ рд▓рд┐рдП рд╕рд░реНрд╡рд░ рд╕реЗрдЯ рдХрд░рддреЗ рд╣реИрдВ:

/* Snippet 1 */ require('dotenv').config(); // To grab env vers from the .env file const express = require('express'); const bodyParser = require('body-parser'); const axios = require('axios'); const qs = require('qs'); const app = express(); // The next two lines will be modified later app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); const server = app.listen(5000); // port 

рдЗрд╕рд╕реЗ рдкрд╣рд▓реЗ рдХрд┐ рд╣рдо рдЬрд╛рд░реА рд░рдЦреЗрдВ, рдЖрдЗрдП рд╣рдо рдЙрд╕ рдЖрд░реЗрдЦ рдкрд░ рдПрдХ рдирдЬрд╝рд░ рдбрд╛рд▓рддреЗ рд╣реИрдВ, рдЬрд┐рд╕рдХреЗ рдмрд╛рд░реЗ рдореЗрдВ рд╣рдо рд╕рдм рдХреБрдЫ рдмрддрд╛ рд░рд╣реЗ рд╣реИрдВред



рдкреНрд░рддреНрдпреЗрдХ рдереНрд░реЗрдб рдкреНрд░рд╛рд░рдВрдн рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИ рдЬрдм рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рд╕рдВрджреЗрд╢ рдореЗрдиреВ рдореЗрдВ рд╕реНрдерд┐рдд рдХреНрд░рд┐рдпрд╛ рдХрд░рддрд╛ рд╣реИред рдЬреИрд╕реЗ рд╣реА рдореИрд╕реЗрдЬ_рдПрдХреНрд╢рди рдЗрд╡реЗрдВрдЯ рдореЗрдВ рдЖрдЧ рд▓рдЧ рдЬрд╛рддреА рд╣реИ, рд╕реНрд▓реИрдХ рдкреЗ-рд▓реЛрдб рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдХреЛ рдЙрд╕ рдЕрдиреБрд░реЛрдз URL рдкрд░ рднреЗрдЬрддрд╛ рд╣реИ рдЬреЛ рдкрд╣рд▓реЗ рдкрдВрдЬреАрдХреГрдд рдерд╛ред



рд╕рдорд╛рдкрди рдмрд┐рдВрджреБ рдЗрд╕ рдкреНрд░рдХрд╛рд░ рд▓рд┐рдЦрд╛ рдЬрд╛ рд╕рдХрддрд╛ рд╣реИ:

 /* Snippet 2 */ app.post('/actions', (req, res) => { const payload = JSON.parse(req.body.payload); const {type, user, submission} = payload; // Verifying the request. I'll explain this later in this tutorial! if (!signature.isVerified(req)) { res.sendStatus(404); return; } if(type === 'message_action') { // open a dialog! } else if (type === 'dialog_submission') { // dialog is submitted } }); 

рдпрджрд┐ рдИрд╡реЗрдВрдЯ рдкреНрд░рдХрд╛рд░ рдореИрд╕реЗрдЬ_рдРрдХреНрд╢рди рд╣реИ, рддреЛ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рдПрдХ рдбрд╛рдпрд▓реЙрдЧ рдЦреЛрд▓рддрд╛ рд╣реИред

рдлрд┐рд░ рд╣рдо рдЙрд╕ рдХреЛрдб рдХреЛ рдЬреЛрдбрд╝рддреЗ рд╣реИрдВ рдЬреЛ рд╕рдВрд╡рд╛рдж рд╕рд╛рдордЧреНрд░реА рдХреА рд╕рдВрд░рдЪрдирд╛ рдХреЛ рдкрд░рд┐рднрд╛рд╖рд┐рдд рдХрд░рддрд╛ рд╣реИ, рдЬрд┐рд╕рдореЗрдВ рд╕реНрд▓реИрдХ рдХреНрд▓рд╛рдЗрдВрдЯ рдореЗрдВ рдЦреБрд▓рддрд╛ рд╣реИ, рдЬрд┐рд╕рдореЗрдВ рдбрд╛рдпрд▓реЙрдЧ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд┐рдпрд╛ рдЬрд╛рддрд╛ рд╣реИред

 /* Snippet 2.1 */ const dialogData = { token: process.env.SLACK_ACCESS_TOKEN, trigger_id: payload.trigger_id, dialog: JSON.stringify({ title: 'Save it to ClipIt!', callback_id: 'clipit', submit_label: 'ClipIt', elements: [ { label: 'Message Text', type: 'textarea', name: 'message', value: payload.message.text }, { label: 'Importance', type: 'select', name: 'importance', value: 'Medium ', options: [ { label: 'High', value: 'High ' }, { label: 'Medium', value: 'Medium ' }, { label: 'Low', value: 'Low я╕П' } ], }, ] }) }; // open the dialog by calling the dialogs.open method and sending the payload axios.post('https://slack.com/api/dialog.open', qs.stringify(dialogData)) .then((result) => { if(result.data.error) { res.sendStatus(500); } else { res.sendStatus(200); } }) .catch((err) => { res.sendStatus(500); }); 

рдпрд╣рд╛рдВ рд╣рдо рд╕реНрд▓реИрдХ рдХреЗ рд▓рд┐рдП рдПрдХ POST рдЕрдиреБрд░реЛрдз рдХреЛ рдирд┐рд╖реНрдкрд╛рджрд┐рдд рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЕрдХреНрд╖рддрдВрддреБ рдореЙрдбреНрдпреВрд▓ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рддреЗ рд╣реИрдВ; рдЙрд╕рдХреЗ рдмрд╛рдж, рдбрд╛рдпрд▓реЙрдЧ.рдкреЗрди рд╡рд┐рдзрд┐ 200 рдХрд╛ HTTP рд╕реНрдЯреЗрдЯрд╕ рднреЗрдЬрдХрд░ рдПрдХ рдбрд╛рдпрд▓реЙрдЧ рдЦреЛрд▓рддрд╛ рд╣реИред



рдпрджрд┐ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рджреНрд╡рд╛рд░рд╛ рд╕рдВрд╡рд╛рдж рдЖрд░рдВрдн рдХрд┐рдпрд╛ рдЧрдпрд╛ рд╣реИ, рддреЛ рд╕рдорд╛рдкрди рдмрд┐рдВрджреБ рднреА рд╕рдХреНрд░рд┐рдп рд╣реИред рд╕реНрдирд┐рдкреЗрдЯ 2 рдХреЛрдб рдореЗрдВ, рдЖрдкрдХреЛ рдЦрд╛рд▓реА HTTP 200 рдЕрдиреБрд░реЛрдз рдХреЗ рд╕рд╛рде рдкреНрд░рддрд┐рдХреНрд░рд┐рдпрд╛ рдХрд░рдиреЗ рдХреА рдЖрд╡рд╢реНрдпрдХрддрд╛ рд╣реИ рддрд╛рдХрд┐ рд╕реНрд▓реИрдХ рдХреЛ рдкрддрд╛ рдЪрд▓реЗ рдХрд┐ рджреГрд╢реНрдп рдкреНрд░рд╛рдкреНрдд рд╣реЛ рдЧрдпрд╛ рд╣реИред

рдЕрдВрдд рдореЗрдВ, рд╣рдо chat.postMessage рдкрджреНрдзрддрд┐ рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдХреЗ рдЙрдкрдпреЛрдЧрдХрд░реНрддрд╛ рдХреЛ рдПрдХ рдкреБрд╖реНрдЯрд┐рдХрд░рдг рд╕рдВрджреЗрд╢ рднреЗрдЬрддреЗ рд╣реИрдВред

 /* Snippet 2.2 */ else if (type === 'dialog_submission') { res.send(''); // Save the data in DB db.set(user.id, submission); // this is a pseudo-code! // DM the user a confirmation message const attachments = [ { title: 'Message clipped!', title_link: `http://example.com/${user.id}/clip`, fields: [ { title: 'Message', value: submission.message }, { title: 'Importance', value: submission.importance, short: true }, ], }, ]; const message = { token: process.env.SLACK_ACCESS_TOKEN, channel: user.id, as_user: true, // DM will be sent by the bot attachments: JSON.stringify(attachments) }; } 

рдЕрдм рдХреЛрдб рдЪрд▓рд╛рддреЗ рд╣реИрдВ рдФрд░ рджреЗрдЦрддреЗ рд╣реИрдВ рдХрд┐ рдПрдкреНрд▓рд┐рдХреЗрд╢рди рд╕реНрд▓реИрдХ рдХреЗ рд╕рд╛рде рдПрдХреАрдХрд░рдг рдореЗрдВ рдХреИрд╕реЗ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИред рдЕрдЧрд░ рд╕рдм рдареАрдХ рд╣реИ, рддреЛ рдЖрдЦрд┐рд░реА рдХрджрдо рдЙрдард╛рдПрдВред

рд╕рддреНрдпрд╛рдкрди рдХрд╛ рдЕрдиреБрд░реЛрдз рдХрд░реЗрдВ


рдЕрдм рдЖрд╡реЗрджрди рд╕реБрд░рдХреНрд╖рд╛ рдХрд╛ рдзреНрдпрд╛рди рд░рдЦреЗрдВ рдФрд░ рдЕрдиреБрд░реЛрдз рд╕рддреНрдпрд╛рдкрди рдЬреЛрдбрд╝реЗрдВред рд╣рдореЗрд╢рд╛ рдЙрдиреНрд╣реЗрдВ рдХрд╛рдо рдкрд░ рд▓реЗрдиреЗ рд╕реЗ рдкрд╣рд▓реЗ рд╕реНрд▓реИрдХ рд╕реЗ рдХрд┐рд╕реА рднреА рдЕрдиреБрд░реЛрдз рдХреЛ рд╕рддреНрдпрд╛рдкрд┐рдд рдХрд░реЗрдВред

рдРрд╕рд╛ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП, рд╕реНрдирд┐рдкреЗрдЯ 1 рдкрд░ рд╡рд╛рдкрд╕ рдЬрд╛рдПрдВ, рдЬреЛ рдмрд╣реБрдд рдКрдкрд░ рд╕реНрдерд┐рдд рд╣реИред рдЙрд╕ рдЬрдЧрд╣ рдХреЛ рдмрджрд▓реЗрдВ рдЬрд╣рд╛рдБ рдПрдХ рдЯрд┐рдкреНрдкрдгреА рд╣реИ // рдЕрдЧрд▓реА рджреЛ рдкрдВрдХреНрддрд┐рдпреЛрдВ рдХреЛ рдмрд╛рдж рдореЗрдВ рд╕рдВрд╢реЛрдзрд┐рдд рдХрд┐рдпрд╛ рдЬрд╛рдПрдЧрд╛, рдЗрд╕рдХреЗ рд╕рд╛рде:

 /* Snippet 3 */ const rawBodyBuffer = (req, res, buf, encoding) => { if (buf && buf.length) { req.rawBody = buf.toString(encoding || 'utf8'); } }; app.use(bodyParser.urlencoded({verify: rawBodyBuffer, extended: true })); app.use(bodyParser.json({ verify: rawBodyBuffer })); 

рдореИрдВрдиреЗ рдкрд╣рд▓реЗ рд╕реЗ рд╣реА рд╕рддреНрдпрд╛рдкрди рдореЗрдВ рдХреНрд░рд┐рдкреНрдЯреЛрдЧреНрд░рд╛рдлреА рдХреЛ VerSignature.js рдХреЗ рд▓рд┐рдП рд╕рдХреНрд╖рдо рдХрд┐рдпрд╛ рд╣реИ, рдЗрд╕рд▓рд┐рдП рдХреЗрд╡рд▓ index.js рдХреА рд╢реБрд░реБрдЖрдд рдореЗрдВ рдПрдХ рдлрд╝рдВрдХреНрд╢рди рдЬреЛрдбрд╝реЗрдВ:

 const signature = require('./verifySignature'); 

рдЕрдм рд╣рдо рд╕рддреНрдпрд╛рдкрди рдХрд░рддреЗ рд╣реИрдВ:

 if(!signature.isVerified(req)) { // when the request is NOT coming from Slack! res.sendStatus(404); // a good idea to just make it тАЬnot foundтАЭ to the potential attacker! return; } 

рдореИрдВ рд╣рд░ рдмрд╛рд░ рдЖрдкрдХреЗ рдЖрд╡реЗрджрди рдХреЛ рд╕реНрд▓реИрдХ рд╕реЗ рдкреНрд░рд╛рдкреНрдд рдХрд░рдиреЗ рдХреЗ рдЕрдиреБрд░реЛрдзреЛрдВ рдХреА рдкреБрд╖реНрдЯрд┐ рдХрд░рдиреЗ рдХреА рд╕рд▓рд╛рд╣ рджреЗрддрд╛ рд╣реВрдВред

рд╣рдо рдХреЛрдб рдХреЛ рдлрд┐рд░ рд╕реЗ рдЪрд▓рд╛рддреЗ рд╣реИрдВ, рдФрд░ рдЕрдЧрд░ рд╕рдм рдХреБрдЫ рдХрд╛рдо рдХрд░рддрд╛ рд╣реИ, рддреЛ рдЖрдкрдХреЛ рд╕рдлрд▓рддрд╛ рдХрд╛ рдЬрд╢реНрди рдордирд╛ рд╕рдХрддреЗ рд╣реИрдВ! рдмрдзрд╛рдИ!

Source: https://habr.com/ru/post/hi431346/


All Articles