We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Anonymous gists are going away. Help module will break.
GitHub [announced](https://blog.github.com/2018-02-18-deprecation-notice-removing-anonymous-gist-creation/) last month that anonymous gist creation will be disabled on March 19, 2018 (UTC).

The `help` module was modified in fa89eb7 to essentially paste its output to a gist and simply return the link, and this functionality will break (as of writing) tomorrow. `.help` (or `.commands`) will return an error until (and unless) the module is updated to paste its contents elsewhere and/or create a static HTML file instead as I originally proposed in #1080.

(As an aside, I've been meaning to implement that HTML mode for ages. Maybe this is the kick I needed to finally get off my arse and write it. We'll see.)

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `sopel/modules/help.py`
Content:
```
1 # coding=utf-8
2 """
3 help.py - Sopel Help Module
4 Copyright 2008, Sean B. Palmer, inamidst.com
5 Copyright © 2013, Elad Alfassa, <elad@fedoraproject.org>
6 Licensed under the Eiffel Forum License 2.
7 
8 http://sopel.chat
9 """
10 from __future__ import unicode_literals, absolute_import, print_function, division
11 
12 import textwrap
13 import collections
14 import json
15 
16 import requests
17 
18 from sopel.logger import get_logger
19 from sopel.module import commands, rule, example, priority
20 
21 logger = get_logger(__name__)
22 
23 
24 @rule('$nick' '(?i)(help|doc) +([A-Za-z]+)(?:\?+)?$')
25 @example('.help tell')
26 @commands('help', 'commands')
27 @priority('low')
28 def help(bot, trigger):
29     """Shows a command's documentation, and possibly an example."""
30     if trigger.group(2):
31         name = trigger.group(2)
32         name = name.lower()
33 
34         # number of lines of help to show
35         threshold = 3
36 
37         if name in bot.doc:
38             if len(bot.doc[name][0]) + (1 if bot.doc[name][1] else 0) > threshold:
39                 if trigger.nick != trigger.sender:  # don't say that if asked in private
40                     bot.reply('The documentation for this command is too long; I\'m sending it to you in a private message.')
41                 msgfun = lambda l: bot.msg(trigger.nick, l)
42             else:
43                 msgfun = bot.reply
44 
45             for line in bot.doc[name][0]:
46                 msgfun(line)
47             if bot.doc[name][1]:
48                 msgfun('e.g. ' + bot.doc[name][1])
49     else:
50         # This'll probably catch most cases, without having to spend the time
51         # actually creating the list first. Maybe worth storing the link and a
52         # heuristic in config, too, so it persists across restarts. Would need a
53         # command to regenerate, too...
54         if 'command-gist' in bot.memory and bot.memory['command-gist'][0] == len(bot.command_groups):
55             url = bot.memory['command-gist'][1]
56         else:
57             bot.say("Hang on, I'm creating a list.")
58             msgs = []
59 
60             name_length = max(6, max(len(k) for k in bot.command_groups.keys()))
61             for category, cmds in collections.OrderedDict(sorted(bot.command_groups.items())).items():
62                 category = category.upper().ljust(name_length)
63                 cmds = set(cmds)  # remove duplicates
64                 cmds = '  '.join(cmds)
65                 msg = category + '  ' + cmds
66                 indent = ' ' * (name_length + 2)
67                 # Honestly not sure why this is a list here
68                 msgs.append('\n'.join(textwrap.wrap(msg, subsequent_indent=indent)))
69 
70             url = create_gist(bot, '\n\n'.join(msgs))
71             if not url:
72                 return
73             bot.memory['command-gist'] = (len(bot.command_groups), url)
74         bot.say("I've posted a list of my commands at {} - You can see "
75                 "more info about any of these commands by doing .help "
76                 "<command> (e.g. .help time)".format(url))
77 
78 
79 def create_gist(bot, msg):
80     payload = {
81         'description': 'Command listing for {}@{}'.format(bot.nick, bot.config.core.host),
82         'public': 'true',
83         'files': {
84             'commands.txt': {
85                 "content": msg,
86             },
87         },
88     }
89     try:
90         result = requests.post('https://api.github.com/gists',
91                                data=json.dumps(payload))
92     except requests.RequestException:
93         bot.say("Sorry! Something went wrong.")
94         logger.exception("Error posting commands gist")
95         return
96     if not result.status_code != '201':
97         bot.say("Sorry! Something went wrong.")
98         logger.error("Error %s posting commands gist: %s",
99                      result.status_code, result.text)
100         return
101     result = result.json()
102     if 'html_url' not in result:
103         bot.say("Sorry! Something went wrong.")
104         logger.error("Invalid result %s", result)
105         return
106     return result['html_url']
107 
108 
109 @rule('$nick' r'(?i)help(?:[?!]+)?$')
110 @priority('low')
111 def help2(bot, trigger):
112     response = (
113         'Hi, I\'m a bot. Say ".commands" to me in private for a list ' +
114         'of my commands, or see http://sopel.chat for more ' +
115         'general details. My owner is %s.'
116     ) % bot.config.core.owner
117     bot.reply(response)
118
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/sopel/modules/help.py b/sopel/modules/help.py
--- a/sopel/modules/help.py
+++ b/sopel/modules/help.py
@@ -3,6 +3,7 @@
 help.py - Sopel Help Module
 Copyright 2008, Sean B. Palmer, inamidst.com
 Copyright © 2013, Elad Alfassa, <elad@fedoraproject.org>
+Copyright © 2018, Adam Erdman, pandorah.org
 Licensed under the Eiffel Forum License 2.
 
 http://sopel.chat
@@ -11,8 +12,6 @@
 
 import textwrap
 import collections
-import json
-
 import requests
 
 from sopel.logger import get_logger
@@ -51,8 +50,8 @@
         # actually creating the list first. Maybe worth storing the link and a
         # heuristic in config, too, so it persists across restarts. Would need a
         # command to regenerate, too...
-        if 'command-gist' in bot.memory and bot.memory['command-gist'][0] == len(bot.command_groups):
-            url = bot.memory['command-gist'][1]
+        if 'command-list' in bot.memory and bot.memory['command-list'][0] == len(bot.command_groups):
+            url = bot.memory['command-list'][1]
         else:
             bot.say("Hang on, I'm creating a list.")
             msgs = []
@@ -60,49 +59,39 @@
             name_length = max(6, max(len(k) for k in bot.command_groups.keys()))
             for category, cmds in collections.OrderedDict(sorted(bot.command_groups.items())).items():
                 category = category.upper().ljust(name_length)
+                cmds = set(cmds)  # remove duplicates
                 cmds = '  '.join(cmds)
                 msg = category + '  ' + cmds
                 indent = ' ' * (name_length + 2)
                 # Honestly not sure why this is a list here
                 msgs.append('\n'.join(textwrap.wrap(msg, subsequent_indent=indent)))
 
-            url = create_gist(bot, '\n\n'.join(msgs))
+            url = create_list(bot, '\n\n'.join(msgs))
             if not url:
                 return
-            bot.memory['command-gist'] = (len(bot.command_groups), url)
+            bot.memory['command-list'] = (len(bot.command_groups), url)
         bot.say("I've posted a list of my commands at {} - You can see "
                 "more info about any of these commands by doing .help "
                 "<command> (e.g. .help time)".format(url))
 
 
-def create_gist(bot, msg):
-    payload = {
-        'description': 'Command listing for {}@{}'.format(bot.nick, bot.config.core.host),
-        'public': 'true',
-        'files': {
-            'commands.txt': {
-                "content": msg,
-            },
-        },
-    }
+def create_list(bot, msg):
+    msg = 'Command listing for {}@{}\n\n'.format(bot.nick, bot.config.core.host) + msg
+    payload = { "content": msg }
+    headers = {'Content-type': 'application/json', 'Accept': 'application/json'}
+    
     try:
-        result = requests.post('https://api.github.com/gists',
-                               data=json.dumps(payload))
+        result = requests.post('https://ptpb.pw/', json=payload, headers=headers)
     except requests.RequestException:
         bot.say("Sorry! Something went wrong.")
-        logger.exception("Error posting commands gist")
-        return
-    if not result.status_code != '201':
-        bot.say("Sorry! Something went wrong.")
-        logger.error("Error %s posting commands gist: %s",
-                     result.status_code, result.text)
+        logger.exception("Error posting commands")
         return
     result = result.json()
-    if 'html_url' not in result:
+    if 'url' not in result:
         bot.say("Sorry! Something went wrong.")
         logger.error("Invalid result %s", result)
         return
-    return result['html_url']
+    return result['url']
 
 
 @rule('$nick' r'(?i)help(?:[?!]+)?$')

{"golden_diff": "diff --git a/sopel/modules/help.py b/sopel/modules/help.py\n--- a/sopel/modules/help.py\n+++ b/sopel/modules/help.py\n@@ -3,6 +3,7 @@\n help.py - Sopel Help Module\n Copyright 2008, Sean B. Palmer, inamidst.com\n Copyright \u00a9 2013, Elad Alfassa, <elad@fedoraproject.org>\n+Copyright \u00a9 2018, Adam Erdman, pandorah.org\n Licensed under the Eiffel Forum License 2.\n \n http://sopel.chat\n@@ -11,8 +12,6 @@\n \n import textwrap\n import collections\n-import json\n-\n import requests\n \n from sopel.logger import get_logger\n@@ -51,8 +50,8 @@\n         # actually creating the list first. Maybe worth storing the link and a\n         # heuristic in config, too, so it persists across restarts. Would need a\n         # command to regenerate, too...\n-        if 'command-gist' in bot.memory and bot.memory['command-gist'][0] == len(bot.command_groups):\n-            url = bot.memory['command-gist'][1]\n+        if 'command-list' in bot.memory and bot.memory['command-list'][0] == len(bot.command_groups):\n+            url = bot.memory['command-list'][1]\n         else:\n             bot.say(\"Hang on, I'm creating a list.\")\n             msgs = []\n@@ -60,49 +59,39 @@\n             name_length = max(6, max(len(k) for k in bot.command_groups.keys()))\n             for category, cmds in collections.OrderedDict(sorted(bot.command_groups.items())).items():\n                 category = category.upper().ljust(name_length)\n+                cmds = set(cmds)  # remove duplicates\n                 cmds = '  '.join(cmds)\n                 msg = category + '  ' + cmds\n                 indent = ' ' * (name_length + 2)\n                 # Honestly not sure why this is a list here\n                 msgs.append('\\n'.join(textwrap.wrap(msg, subsequent_indent=indent)))\n \n-            url = create_gist(bot, '\\n\\n'.join(msgs))\n+            url = create_list(bot, '\\n\\n'.join(msgs))\n             if not url:\n                 return\n-            bot.memory['command-gist'] = (len(bot.command_groups), url)\n+            bot.memory['command-list'] = (len(bot.command_groups), url)\n         bot.say(\"I've posted a list of my commands at {} - You can see \"\n                 \"more info about any of these commands by doing .help \"\n                 \"<command> (e.g. .help time)\".format(url))\n \n \n-def create_gist(bot, msg):\n-    payload = {\n-        'description': 'Command listing for {}@{}'.format(bot.nick, bot.config.core.host),\n-        'public': 'true',\n-        'files': {\n-            'commands.txt': {\n-                \"content\": msg,\n-            },\n-        },\n-    }\n+def create_list(bot, msg):\n+    msg = 'Command listing for {}@{}\\n\\n'.format(bot.nick, bot.config.core.host) + msg\n+    payload = { \"content\": msg }\n+    headers = {'Content-type': 'application/json', 'Accept': 'application/json'}\n+    \n     try:\n-        result = requests.post('https://api.github.com/gists',\n-                               data=json.dumps(payload))\n+        result = requests.post('https://ptpb.pw/', json=payload, headers=headers)\n     except requests.RequestException:\n         bot.say(\"Sorry! Something went wrong.\")\n-        logger.exception(\"Error posting commands gist\")\n-        return\n-    if not result.status_code != '201':\n-        bot.say(\"Sorry! Something went wrong.\")\n-        logger.error(\"Error %s posting commands gist: %s\",\n-                     result.status_code, result.text)\n+        logger.exception(\"Error posting commands\")\n         return\n     result = result.json()\n-    if 'html_url' not in result:\n+    if 'url' not in result:\n         bot.say(\"Sorry! Something went wrong.\")\n         logger.error(\"Invalid result %s\", result)\n         return\n-    return result['html_url']\n+    return result['url']\n \n \n @rule('$nick' r'(?i)help(?:[?!]+)?$')\n", "issue": "Anonymous gists are going away. Help module will break.\nGitHub [announced](https://blog.github.com/2018-02-18-deprecation-notice-removing-anonymous-gist-creation/) last month that anonymous gist creation will be disabled on March 19, 2018 (UTC).\r\n\r\nThe `help` module was modified in fa89eb7 to essentially paste its output to a gist and simply return the link, and this functionality will break (as of writing) tomorrow. `.help` (or `.commands`) will return an error until (and unless) the module is updated to paste its contents elsewhere and/or create a static HTML file instead as I originally proposed in #1080.\r\n\r\n(As an aside, I've been meaning to implement that HTML mode for ages. Maybe this is the kick I needed to finally get off my arse and write it. We'll see.)\n", "before_files": [{"content": "# coding=utf-8\n\"\"\"\nhelp.py - Sopel Help Module\nCopyright 2008, Sean B. Palmer, inamidst.com\nCopyright \u00a9 2013, Elad Alfassa, <elad@fedoraproject.org>\nLicensed under the Eiffel Forum License 2.\n\nhttp://sopel.chat\n\"\"\"\nfrom __future__ import unicode_literals, absolute_import, print_function, division\n\nimport textwrap\nimport collections\nimport json\n\nimport requests\n\nfrom sopel.logger import get_logger\nfrom sopel.module import commands, rule, example, priority\n\nlogger = get_logger(__name__)\n\n\n@rule('$nick' '(?i)(help|doc) +([A-Za-z]+)(?:\\?+)?$')\n@example('.help tell')\n@commands('help', 'commands')\n@priority('low')\ndef help(bot, trigger):\n    \"\"\"Shows a command's documentation, and possibly an example.\"\"\"\n    if trigger.group(2):\n        name = trigger.group(2)\n        name = name.lower()\n\n        # number of lines of help to show\n        threshold = 3\n\n        if name in bot.doc:\n            if len(bot.doc[name][0]) + (1 if bot.doc[name][1] else 0) > threshold:\n                if trigger.nick != trigger.sender:  # don't say that if asked in private\n                    bot.reply('The documentation for this command is too long; I\\'m sending it to you in a private message.')\n                msgfun = lambda l: bot.msg(trigger.nick, l)\n            else:\n                msgfun = bot.reply\n\n            for line in bot.doc[name][0]:\n                msgfun(line)\n            if bot.doc[name][1]:\n                msgfun('e.g. ' + bot.doc[name][1])\n    else:\n        # This'll probably catch most cases, without having to spend the time\n        # actually creating the list first. Maybe worth storing the link and a\n        # heuristic in config, too, so it persists across restarts. Would need a\n        # command to regenerate, too...\n        if 'command-gist' in bot.memory and bot.memory['command-gist'][0] == len(bot.command_groups):\n            url = bot.memory['command-gist'][1]\n        else:\n            bot.say(\"Hang on, I'm creating a list.\")\n            msgs = []\n\n            name_length = max(6, max(len(k) for k in bot.command_groups.keys()))\n            for category, cmds in collections.OrderedDict(sorted(bot.command_groups.items())).items():\n                category = category.upper().ljust(name_length)\n                cmds = set(cmds)  # remove duplicates\n                cmds = '  '.join(cmds)\n                msg = category + '  ' + cmds\n                indent = ' ' * (name_length + 2)\n                # Honestly not sure why this is a list here\n                msgs.append('\\n'.join(textwrap.wrap(msg, subsequent_indent=indent)))\n\n            url = create_gist(bot, '\\n\\n'.join(msgs))\n            if not url:\n                return\n            bot.memory['command-gist'] = (len(bot.command_groups), url)\n        bot.say(\"I've posted a list of my commands at {} - You can see \"\n                \"more info about any of these commands by doing .help \"\n                \"<command> (e.g. .help time)\".format(url))\n\n\ndef create_gist(bot, msg):\n    payload = {\n        'description': 'Command listing for {}@{}'.format(bot.nick, bot.config.core.host),\n        'public': 'true',\n        'files': {\n            'commands.txt': {\n                \"content\": msg,\n            },\n        },\n    }\n    try:\n        result = requests.post('https://api.github.com/gists',\n                               data=json.dumps(payload))\n    except requests.RequestException:\n        bot.say(\"Sorry! Something went wrong.\")\n        logger.exception(\"Error posting commands gist\")\n        return\n    if not result.status_code != '201':\n        bot.say(\"Sorry! Something went wrong.\")\n        logger.error(\"Error %s posting commands gist: %s\",\n                     result.status_code, result.text)\n        return\n    result = result.json()\n    if 'html_url' not in result:\n        bot.say(\"Sorry! Something went wrong.\")\n        logger.error(\"Invalid result %s\", result)\n        return\n    return result['html_url']\n\n\n@rule('$nick' r'(?i)help(?:[?!]+)?$')\n@priority('low')\ndef help2(bot, trigger):\n    response = (\n        'Hi, I\\'m a bot. Say \".commands\" to me in private for a list ' +\n        'of my commands, or see http://sopel.chat for more ' +\n        'general details. My owner is %s.'\n    ) % bot.config.core.owner\n    bot.reply(response)\n", "path": "sopel/modules/help.py"}], "after_files": [{"content": "# coding=utf-8\n\"\"\"\nhelp.py - Sopel Help Module\nCopyright 2008, Sean B. Palmer, inamidst.com\nCopyright \u00a9 2013, Elad Alfassa, <elad@fedoraproject.org>\nCopyright \u00a9 2018, Adam Erdman, pandorah.org\nLicensed under the Eiffel Forum License 2.\n\nhttp://sopel.chat\n\"\"\"\nfrom __future__ import unicode_literals, absolute_import, print_function, division\n\nimport textwrap\nimport collections\nimport requests\n\nfrom sopel.logger import get_logger\nfrom sopel.module import commands, rule, example, priority\n\nlogger = get_logger(__name__)\n\n\n@rule('$nick' '(?i)(help|doc) +([A-Za-z]+)(?:\\?+)?$')\n@example('.help tell')\n@commands('help', 'commands')\n@priority('low')\ndef help(bot, trigger):\n    \"\"\"Shows a command's documentation, and possibly an example.\"\"\"\n    if trigger.group(2):\n        name = trigger.group(2)\n        name = name.lower()\n\n        # number of lines of help to show\n        threshold = 3\n\n        if name in bot.doc:\n            if len(bot.doc[name][0]) + (1 if bot.doc[name][1] else 0) > threshold:\n                if trigger.nick != trigger.sender:  # don't say that if asked in private\n                    bot.reply('The documentation for this command is too long; I\\'m sending it to you in a private message.')\n                msgfun = lambda l: bot.msg(trigger.nick, l)\n            else:\n                msgfun = bot.reply\n\n            for line in bot.doc[name][0]:\n                msgfun(line)\n            if bot.doc[name][1]:\n                msgfun('e.g. ' + bot.doc[name][1])\n    else:\n        # This'll probably catch most cases, without having to spend the time\n        # actually creating the list first. Maybe worth storing the link and a\n        # heuristic in config, too, so it persists across restarts. Would need a\n        # command to regenerate, too...\n        if 'command-list' in bot.memory and bot.memory['command-list'][0] == len(bot.command_groups):\n            url = bot.memory['command-list'][1]\n        else:\n            bot.say(\"Hang on, I'm creating a list.\")\n            msgs = []\n\n            name_length = max(6, max(len(k) for k in bot.command_groups.keys()))\n            for category, cmds in collections.OrderedDict(sorted(bot.command_groups.items())).items():\n                category = category.upper().ljust(name_length)\n                cmds = set(cmds)  # remove duplicates\n                cmds = '  '.join(cmds)\n                msg = category + '  ' + cmds\n                indent = ' ' * (name_length + 2)\n                # Honestly not sure why this is a list here\n                msgs.append('\\n'.join(textwrap.wrap(msg, subsequent_indent=indent)))\n\n            url = create_list(bot, '\\n\\n'.join(msgs))\n            if not url:\n                return\n            bot.memory['command-list'] = (len(bot.command_groups), url)\n        bot.say(\"I've posted a list of my commands at {} - You can see \"\n                \"more info about any of these commands by doing .help \"\n                \"<command> (e.g. .help time)\".format(url))\n\n\ndef create_list(bot, msg):\n    msg = 'Command listing for {}@{}\\n\\n'.format(bot.nick, bot.config.core.host) + msg\n    payload = { \"content\": msg }\n    headers = {'Content-type': 'application/json', 'Accept': 'application/json'}\n    \n    try:\n        result = requests.post('https://ptpb.pw/', json=payload, headers=headers)\n    except requests.RequestException:\n        bot.say(\"Sorry! Something went wrong.\")\n        logger.exception(\"Error posting commands\")\n        return\n    result = result.json()\n    if 'url' not in result:\n        bot.say(\"Sorry! Something went wrong.\")\n        logger.error(\"Invalid result %s\", result)\n        return\n    return result['url']\n\n\n@rule('$nick' r'(?i)help(?:[?!]+)?$')\n@priority('low')\ndef help2(bot, trigger):\n    response = (\n        'Hi, I\\'m a bot. Say \".commands\" to me in private for a list ' +\n        'of my commands, or see http://sopel.chat for more ' +\n        'general details. My owner is %s.'\n    ) % bot.config.core.owner\n    bot.reply(response)\n", "path": "sopel/modules/help.py"}]}