We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
[chanlogs] Missing copyright header
@singingwolfboy could you add the copyright header to comply with the [EFL](https://github.com/embolalia/willie/blob/master/COPYING)? Below is the required lay-out:

```
# coding=utf-8
"""
chanlogs.py - Willie Channel Logger Module
Copyright 2014, FIRSTNAME LASTNAME <EMAIL>

Licensed under the Eiffel Forum License 2.

http://willie.dftba.net
"""
```


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `willie/modules/chanlogs.py`
Content:
```
1 #coding: utf8
2 """
3 Channel logger
4 """
5 from __future__ import unicode_literals
6 import os
7 import os.path
8 import threading
9 from datetime import datetime
10 import willie.module
11 import willie.tools
12 from willie.config import ConfigurationError
13 
14 MESSAGE_TPL = "{datetime}  <{origin.nick}> {message}"
15 ACTION_TPL = "{datetime}  * {origin.nick} {message}"
16 NICK_TPL = "{datetime}  *** {origin.nick} is now known as {origin.sender}"
17 JOIN_TPL = "{datetime}  *** {origin.nick} has joined {trigger}"
18 PART_TPL = "{datetime}  *** {origin.nick} has left {trigger}"
19 QUIT_TPL = "{datetime}  *** {origin.nick} has quit IRC"
20 
21 
22 def configure(config):
23     if config.option("Configure channel logging", False):
24         config.add_section("chanlogs")
25         config.interactive_add(
26             "chanlogs", "dir",
27             "Absolute path to channel log storage directory",
28             default="/home/willie/chanlogs"
29         )
30         config.add_option("chanlogs", "by_day", "Split log files by day", default=True)
31         config.add_option("chanlogs", "privmsg", "Record private messages", default=False)
32         config.add_option("chanlogs", "microseconds", "Microsecond precision", default=False)
33         # could ask if user wants to customize message templates,
34         # but that seems unnecessary
35 
36 
37 def get_fpath(bot, channel=None):
38     """
39     Returns a string corresponding to the path to the file where the message
40     currently being handled should be logged.
41     """
42     basedir = os.path.expanduser(bot.config.chanlogs.dir)
43     channel = channel or bot.origin.sender
44     channel = channel.lstrip("#")
45 
46     dt = datetime.utcnow()
47     if not bot.config.chanlogs.microseconds:
48         dt = dt.replace(microsecond=0)
49     if bot.config.chanlogs.by_day:
50         fname = "{channel}-{date}.log".format(channel=channel, date=dt.date().isoformat())
51     else:
52         fname = "{channel}.log".format(channel=channel)
53     return os.path.join(basedir, fname)
54 
55 
56 def _format_template(tpl, bot, **kwargs):
57     dt = datetime.utcnow()
58     if not bot.config.chanlogs.microseconds:
59         dt = dt.replace(microsecond=0)
60 
61     return tpl.format(
62         origin=bot.origin, datetime=dt.isoformat(),
63         date=dt.date().isoformat(), time=dt.time().isoformat(),
64         **kwargs
65     ) + "\n"
66 
67 
68 def setup(bot):
69     if not getattr(bot.config, "chanlogs", None):
70         raise ConfigurationError("Channel logs are not configured")
71     if not getattr(bot.config.chanlogs, "dir", None):
72         raise ConfigurationError("Channel log storage directory is not defined")
73 
74     # ensure log directory exists
75     basedir = os.path.expanduser(bot.config.chanlogs.dir)
76     if not os.path.exists(basedir):
77         os.makedirs(basedir)
78 
79     # locks for log files
80     if not bot.memory.contains('chanlog_locks'):
81         bot.memory['chanlog_locks'] = willie.tools.WillieMemoryWithDefault(threading.Lock)
82 
83 
84 @willie.module.rule('.*')
85 @willie.module.unblockable
86 def log_message(bot, message):
87     "Log every message in a channel"
88     # if this is a private message and we're not logging those, return early
89     if not bot.origin.sender.startswith("#") and not bot.config.chanlogs.privmsg:
90         return
91 
92     # determine which template we want, message or action
93     if message.startswith("\001ACTION ") and message.endswith("\001"):
94         tpl = bot.config.chanlogs.action_template or ACTION_TPL
95         # strip off start and end
96         message = message[8:-1]
97     else:
98         tpl = bot.config.chanlogs.message_template or MESSAGE_TPL
99 
100     logline = _format_template(tpl, bot, message=message)
101     fpath = get_fpath(bot)
102     with bot.memory['chanlog_locks'][fpath]:
103         with open(fpath, "a") as f:
104             f.write(logline.encode('utf-8'))
105 
106 
107 @willie.module.rule('.*')
108 @willie.module.event("JOIN")
109 @willie.module.unblockable
110 def log_join(bot, trigger):
111     tpl = bot.config.chanlogs.join_template or JOIN_TPL
112     logline = _format_template(tpl, bot, trigger=trigger)
113     fpath = get_fpath(bot, channel=trigger)
114     with bot.memory['chanlog_locks'][fpath]:
115         with open(fpath, "a") as f:
116             f.write(logline.encode('utf-8'))
117 
118 
119 @willie.module.rule('.*')
120 @willie.module.event("PART")
121 @willie.module.unblockable
122 def log_part(bot, trigger):
123     tpl = bot.config.chanlogs.part_template or PART_TPL
124     logline = _format_template(tpl, bot, trigger=trigger)
125     fpath = get_fpath(bot, channel=trigger)
126     with bot.memory['chanlog_locks'][fpath]:
127         with open(fpath, "a") as f:
128             f.write(logline.encode('utf-8'))
129 
130 
131 @willie.module.rule('.*')
132 @willie.module.event("QUIT")
133 @willie.module.unblockable
134 @willie.module.thread(False)
135 @willie.module.priority('high')
136 def log_quit(bot, trigger):
137     tpl = bot.config.chanlogs.quit_template or QUIT_TPL
138     logline = _format_template(tpl, bot, trigger=trigger)
139     # make a copy of bot.privileges that we can safely iterate over
140     privcopy = list(bot.privileges.items())
141     # write logline to *all* channels that the user was present in
142     for channel, privileges in privcopy:
143         if bot.origin.nick in privileges:
144             fpath = get_fpath(bot, channel)
145             with bot.memory['chanlog_locks'][fpath]:
146                 with open(fpath, "a") as f:
147                     f.write(logline.encode('utf-8'))
148 
149 
150 @willie.module.rule('.*')
151 @willie.module.event("NICK")
152 @willie.module.unblockable
153 def log_nick_change(bot, trigger):
154     tpl = bot.config.chanlogs.nick_template or NICK_TPL
155     logline = _format_template(tpl, bot, trigger=trigger)
156     old_nick = bot.origin.nick
157     new_nick = bot.origin.sender
158     # make a copy of bot.privileges that we can safely iterate over
159     privcopy = list(bot.privileges.items())
160     # write logline to *all* channels that the user is present in
161     for channel, privileges in privcopy:
162         if old_nick in privileges or new_nick in privileges:
163             fpath = get_fpath(bot, channel)
164             with bot.memory['chanlog_locks'][fpath]:
165                 with open(fpath, "a") as f:
166                     f.write(logline.encode('utf-8'))
167
```
--- 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/willie/modules/chanlogs.py b/willie/modules/chanlogs.py
--- a/willie/modules/chanlogs.py
+++ b/willie/modules/chanlogs.py
@@ -1,6 +1,11 @@
-#coding: utf8
+# coding=utf-8
 """
-Channel logger
+chanlogs.py - Willie Channel Logger module
+Copyright 2014, David Baumgold <david@davidbaumgold.com>
+
+Licensed under the Eiffel Forum License 2
+
+http://willie.dftba.net
 """
 from __future__ import unicode_literals
 import os

{"golden_diff": "diff --git a/willie/modules/chanlogs.py b/willie/modules/chanlogs.py\n--- a/willie/modules/chanlogs.py\n+++ b/willie/modules/chanlogs.py\n@@ -1,6 +1,11 @@\n-#coding: utf8\n+# coding=utf-8\n \"\"\"\n-Channel logger\n+chanlogs.py - Willie Channel Logger module\n+Copyright 2014, David Baumgold <david@davidbaumgold.com>\n+\n+Licensed under the Eiffel Forum License 2\n+\n+http://willie.dftba.net\n \"\"\"\n from __future__ import unicode_literals\n import os\n", "issue": "[chanlogs] Missing copyright header\n@singingwolfboy could you add the copyright header to comply with the [EFL](https://github.com/embolalia/willie/blob/master/COPYING)? Below is the required lay-out:\n\n```\n# coding=utf-8\n\"\"\"\nchanlogs.py - Willie Channel Logger Module\nCopyright 2014, FIRSTNAME LASTNAME <EMAIL>\n\nLicensed under the Eiffel Forum License 2.\n\nhttp://willie.dftba.net\n\"\"\"\n```\n\n", "before_files": [{"content": "#coding: utf8\n\"\"\"\nChannel logger\n\"\"\"\nfrom __future__ import unicode_literals\nimport os\nimport os.path\nimport threading\nfrom datetime import datetime\nimport willie.module\nimport willie.tools\nfrom willie.config import ConfigurationError\n\nMESSAGE_TPL = \"{datetime}  <{origin.nick}> {message}\"\nACTION_TPL = \"{datetime}  * {origin.nick} {message}\"\nNICK_TPL = \"{datetime}  *** {origin.nick} is now known as {origin.sender}\"\nJOIN_TPL = \"{datetime}  *** {origin.nick} has joined {trigger}\"\nPART_TPL = \"{datetime}  *** {origin.nick} has left {trigger}\"\nQUIT_TPL = \"{datetime}  *** {origin.nick} has quit IRC\"\n\n\ndef configure(config):\n    if config.option(\"Configure channel logging\", False):\n        config.add_section(\"chanlogs\")\n        config.interactive_add(\n            \"chanlogs\", \"dir\",\n            \"Absolute path to channel log storage directory\",\n            default=\"/home/willie/chanlogs\"\n        )\n        config.add_option(\"chanlogs\", \"by_day\", \"Split log files by day\", default=True)\n        config.add_option(\"chanlogs\", \"privmsg\", \"Record private messages\", default=False)\n        config.add_option(\"chanlogs\", \"microseconds\", \"Microsecond precision\", default=False)\n        # could ask if user wants to customize message templates,\n        # but that seems unnecessary\n\n\ndef get_fpath(bot, channel=None):\n    \"\"\"\n    Returns a string corresponding to the path to the file where the message\n    currently being handled should be logged.\n    \"\"\"\n    basedir = os.path.expanduser(bot.config.chanlogs.dir)\n    channel = channel or bot.origin.sender\n    channel = channel.lstrip(\"#\")\n\n    dt = datetime.utcnow()\n    if not bot.config.chanlogs.microseconds:\n        dt = dt.replace(microsecond=0)\n    if bot.config.chanlogs.by_day:\n        fname = \"{channel}-{date}.log\".format(channel=channel, date=dt.date().isoformat())\n    else:\n        fname = \"{channel}.log\".format(channel=channel)\n    return os.path.join(basedir, fname)\n\n\ndef _format_template(tpl, bot, **kwargs):\n    dt = datetime.utcnow()\n    if not bot.config.chanlogs.microseconds:\n        dt = dt.replace(microsecond=0)\n\n    return tpl.format(\n        origin=bot.origin, datetime=dt.isoformat(),\n        date=dt.date().isoformat(), time=dt.time().isoformat(),\n        **kwargs\n    ) + \"\\n\"\n\n\ndef setup(bot):\n    if not getattr(bot.config, \"chanlogs\", None):\n        raise ConfigurationError(\"Channel logs are not configured\")\n    if not getattr(bot.config.chanlogs, \"dir\", None):\n        raise ConfigurationError(\"Channel log storage directory is not defined\")\n\n    # ensure log directory exists\n    basedir = os.path.expanduser(bot.config.chanlogs.dir)\n    if not os.path.exists(basedir):\n        os.makedirs(basedir)\n\n    # locks for log files\n    if not bot.memory.contains('chanlog_locks'):\n        bot.memory['chanlog_locks'] = willie.tools.WillieMemoryWithDefault(threading.Lock)\n\n\n@willie.module.rule('.*')\n@willie.module.unblockable\ndef log_message(bot, message):\n    \"Log every message in a channel\"\n    # if this is a private message and we're not logging those, return early\n    if not bot.origin.sender.startswith(\"#\") and not bot.config.chanlogs.privmsg:\n        return\n\n    # determine which template we want, message or action\n    if message.startswith(\"\\001ACTION \") and message.endswith(\"\\001\"):\n        tpl = bot.config.chanlogs.action_template or ACTION_TPL\n        # strip off start and end\n        message = message[8:-1]\n    else:\n        tpl = bot.config.chanlogs.message_template or MESSAGE_TPL\n\n    logline = _format_template(tpl, bot, message=message)\n    fpath = get_fpath(bot)\n    with bot.memory['chanlog_locks'][fpath]:\n        with open(fpath, \"a\") as f:\n            f.write(logline.encode('utf-8'))\n\n\n@willie.module.rule('.*')\n@willie.module.event(\"JOIN\")\n@willie.module.unblockable\ndef log_join(bot, trigger):\n    tpl = bot.config.chanlogs.join_template or JOIN_TPL\n    logline = _format_template(tpl, bot, trigger=trigger)\n    fpath = get_fpath(bot, channel=trigger)\n    with bot.memory['chanlog_locks'][fpath]:\n        with open(fpath, \"a\") as f:\n            f.write(logline.encode('utf-8'))\n\n\n@willie.module.rule('.*')\n@willie.module.event(\"PART\")\n@willie.module.unblockable\ndef log_part(bot, trigger):\n    tpl = bot.config.chanlogs.part_template or PART_TPL\n    logline = _format_template(tpl, bot, trigger=trigger)\n    fpath = get_fpath(bot, channel=trigger)\n    with bot.memory['chanlog_locks'][fpath]:\n        with open(fpath, \"a\") as f:\n            f.write(logline.encode('utf-8'))\n\n\n@willie.module.rule('.*')\n@willie.module.event(\"QUIT\")\n@willie.module.unblockable\n@willie.module.thread(False)\n@willie.module.priority('high')\ndef log_quit(bot, trigger):\n    tpl = bot.config.chanlogs.quit_template or QUIT_TPL\n    logline = _format_template(tpl, bot, trigger=trigger)\n    # make a copy of bot.privileges that we can safely iterate over\n    privcopy = list(bot.privileges.items())\n    # write logline to *all* channels that the user was present in\n    for channel, privileges in privcopy:\n        if bot.origin.nick in privileges:\n            fpath = get_fpath(bot, channel)\n            with bot.memory['chanlog_locks'][fpath]:\n                with open(fpath, \"a\") as f:\n                    f.write(logline.encode('utf-8'))\n\n\n@willie.module.rule('.*')\n@willie.module.event(\"NICK\")\n@willie.module.unblockable\ndef log_nick_change(bot, trigger):\n    tpl = bot.config.chanlogs.nick_template or NICK_TPL\n    logline = _format_template(tpl, bot, trigger=trigger)\n    old_nick = bot.origin.nick\n    new_nick = bot.origin.sender\n    # make a copy of bot.privileges that we can safely iterate over\n    privcopy = list(bot.privileges.items())\n    # write logline to *all* channels that the user is present in\n    for channel, privileges in privcopy:\n        if old_nick in privileges or new_nick in privileges:\n            fpath = get_fpath(bot, channel)\n            with bot.memory['chanlog_locks'][fpath]:\n                with open(fpath, \"a\") as f:\n                    f.write(logline.encode('utf-8'))\n", "path": "willie/modules/chanlogs.py"}], "after_files": [{"content": "# coding=utf-8\n\"\"\"\nchanlogs.py - Willie Channel Logger module\nCopyright 2014, David Baumgold <david@davidbaumgold.com>\n\nLicensed under the Eiffel Forum License 2\n\nhttp://willie.dftba.net\n\"\"\"\nfrom __future__ import unicode_literals\nimport os\nimport os.path\nimport threading\nfrom datetime import datetime\nimport willie.module\nimport willie.tools\nfrom willie.config import ConfigurationError\n\nMESSAGE_TPL = \"{datetime}  <{origin.nick}> {message}\"\nACTION_TPL = \"{datetime}  * {origin.nick} {message}\"\nNICK_TPL = \"{datetime}  *** {origin.nick} is now known as {origin.sender}\"\nJOIN_TPL = \"{datetime}  *** {origin.nick} has joined {trigger}\"\nPART_TPL = \"{datetime}  *** {origin.nick} has left {trigger}\"\nQUIT_TPL = \"{datetime}  *** {origin.nick} has quit IRC\"\n\n\ndef configure(config):\n    if config.option(\"Configure channel logging\", False):\n        config.add_section(\"chanlogs\")\n        config.interactive_add(\n            \"chanlogs\", \"dir\",\n            \"Absolute path to channel log storage directory\",\n            default=\"/home/willie/chanlogs\"\n        )\n        config.add_option(\"chanlogs\", \"by_day\", \"Split log files by day\", default=True)\n        config.add_option(\"chanlogs\", \"privmsg\", \"Record private messages\", default=False)\n        config.add_option(\"chanlogs\", \"microseconds\", \"Microsecond precision\", default=False)\n        # could ask if user wants to customize message templates,\n        # but that seems unnecessary\n\n\ndef get_fpath(bot, channel=None):\n    \"\"\"\n    Returns a string corresponding to the path to the file where the message\n    currently being handled should be logged.\n    \"\"\"\n    basedir = os.path.expanduser(bot.config.chanlogs.dir)\n    channel = channel or bot.origin.sender\n    channel = channel.lstrip(\"#\")\n\n    dt = datetime.utcnow()\n    if not bot.config.chanlogs.microseconds:\n        dt = dt.replace(microsecond=0)\n    if bot.config.chanlogs.by_day:\n        fname = \"{channel}-{date}.log\".format(channel=channel, date=dt.date().isoformat())\n    else:\n        fname = \"{channel}.log\".format(channel=channel)\n    return os.path.join(basedir, fname)\n\n\ndef _format_template(tpl, bot, **kwargs):\n    dt = datetime.utcnow()\n    if not bot.config.chanlogs.microseconds:\n        dt = dt.replace(microsecond=0)\n\n    return tpl.format(\n        origin=bot.origin, datetime=dt.isoformat(),\n        date=dt.date().isoformat(), time=dt.time().isoformat(),\n        **kwargs\n    ) + \"\\n\"\n\n\ndef setup(bot):\n    if not getattr(bot.config, \"chanlogs\", None):\n        raise ConfigurationError(\"Channel logs are not configured\")\n    if not getattr(bot.config.chanlogs, \"dir\", None):\n        raise ConfigurationError(\"Channel log storage directory is not defined\")\n\n    # ensure log directory exists\n    basedir = os.path.expanduser(bot.config.chanlogs.dir)\n    if not os.path.exists(basedir):\n        os.makedirs(basedir)\n\n    # locks for log files\n    if not bot.memory.contains('chanlog_locks'):\n        bot.memory['chanlog_locks'] = willie.tools.WillieMemoryWithDefault(threading.Lock)\n\n\n@willie.module.rule('.*')\n@willie.module.unblockable\ndef log_message(bot, message):\n    \"Log every message in a channel\"\n    # if this is a private message and we're not logging those, return early\n    if not bot.origin.sender.startswith(\"#\") and not bot.config.chanlogs.privmsg:\n        return\n\n    # determine which template we want, message or action\n    if message.startswith(\"\\001ACTION \") and message.endswith(\"\\001\"):\n        tpl = bot.config.chanlogs.action_template or ACTION_TPL\n        # strip off start and end\n        message = message[8:-1]\n    else:\n        tpl = bot.config.chanlogs.message_template or MESSAGE_TPL\n\n    logline = _format_template(tpl, bot, message=message)\n    fpath = get_fpath(bot)\n    with bot.memory['chanlog_locks'][fpath]:\n        with open(fpath, \"a\") as f:\n            f.write(logline.encode('utf-8'))\n\n\n@willie.module.rule('.*')\n@willie.module.event(\"JOIN\")\n@willie.module.unblockable\ndef log_join(bot, trigger):\n    tpl = bot.config.chanlogs.join_template or JOIN_TPL\n    logline = _format_template(tpl, bot, trigger=trigger)\n    fpath = get_fpath(bot, channel=trigger)\n    with bot.memory['chanlog_locks'][fpath]:\n        with open(fpath, \"a\") as f:\n            f.write(logline.encode('utf-8'))\n\n\n@willie.module.rule('.*')\n@willie.module.event(\"PART\")\n@willie.module.unblockable\ndef log_part(bot, trigger):\n    tpl = bot.config.chanlogs.part_template or PART_TPL\n    logline = _format_template(tpl, bot, trigger=trigger)\n    fpath = get_fpath(bot, channel=trigger)\n    with bot.memory['chanlog_locks'][fpath]:\n        with open(fpath, \"a\") as f:\n            f.write(logline.encode('utf-8'))\n\n\n@willie.module.rule('.*')\n@willie.module.event(\"QUIT\")\n@willie.module.unblockable\n@willie.module.thread(False)\n@willie.module.priority('high')\ndef log_quit(bot, trigger):\n    tpl = bot.config.chanlogs.quit_template or QUIT_TPL\n    logline = _format_template(tpl, bot, trigger=trigger)\n    # make a copy of bot.privileges that we can safely iterate over\n    privcopy = list(bot.privileges.items())\n    # write logline to *all* channels that the user was present in\n    for channel, privileges in privcopy:\n        if bot.origin.nick in privileges:\n            fpath = get_fpath(bot, channel)\n            with bot.memory['chanlog_locks'][fpath]:\n                with open(fpath, \"a\") as f:\n                    f.write(logline.encode('utf-8'))\n\n\n@willie.module.rule('.*')\n@willie.module.event(\"NICK\")\n@willie.module.unblockable\ndef log_nick_change(bot, trigger):\n    tpl = bot.config.chanlogs.nick_template or NICK_TPL\n    logline = _format_template(tpl, bot, trigger=trigger)\n    old_nick = bot.origin.nick\n    new_nick = bot.origin.sender\n    # make a copy of bot.privileges that we can safely iterate over\n    privcopy = list(bot.privileges.items())\n    # write logline to *all* channels that the user is present in\n    for channel, privileges in privcopy:\n        if old_nick in privileges or new_nick in privileges:\n            fpath = get_fpath(bot, channel)\n            with bot.memory['chanlog_locks'][fpath]:\n                with open(fpath, \"a\") as f:\n                    f.write(logline.encode('utf-8'))\n", "path": "willie/modules/chanlogs.py"}]}