A Python robot that edits Wikipedia and interacts with people over IRC https://en.wikipedia.org/wiki/User:EarwigBot
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

преди 11 години
преди 9 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. # -*- coding: utf-8 -*-
  2. #
  3. # Copyright (C) 2009-2015 Ben Kurtovic <ben.kurtovic@gmail.com>
  4. #
  5. # Permission is hereby granted, free of charge, to any person obtaining a copy
  6. # of this software and associated documentation files (the "Software"), to deal
  7. # in the Software without restriction, including without limitation the rights
  8. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. # copies of the Software, and to permit persons to whom the Software is
  10. # furnished to do so, subject to the following conditions:
  11. #
  12. # The above copyright notice and this permission notice shall be included in
  13. # all copies or substantial portions of the Software.
  14. #
  15. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  21. # SOFTWARE.
  22. from fnmatch import fnmatch
  23. import sqlite3 as sqlite
  24. from threading import Lock
  25. __all__ = ["PermissionsDB"]
  26. class PermissionsDB(object):
  27. """
  28. **EarwigBot: Permissions Database Manager**
  29. Controls the :file:`permissions.db` file, which stores the bot's owners and
  30. admins for the purposes of using certain dangerous IRC commands.
  31. """
  32. ADMIN = 1
  33. OWNER = 2
  34. def __init__(self, dbfile):
  35. self._dbfile = dbfile
  36. self._db_access_lock = Lock()
  37. self._users = {}
  38. self._attributes = {}
  39. def __repr__(self):
  40. """Return the canonical string representation of the PermissionsDB."""
  41. res = "PermissionsDB(dbfile={0!r})"
  42. return res.format(self._dbfile)
  43. def __str__(self):
  44. """Return a nice string representation of the PermissionsDB."""
  45. return "<PermissionsDB at {0}>".format(self._dbfile)
  46. def _create(self, conn):
  47. """Initialize the permissions database with its necessary tables."""
  48. query = """CREATE TABLE users (user_nick, user_ident, user_host,
  49. user_rank);
  50. CREATE TABLE attributes (attr_uid, attr_key, attr_value);"""
  51. conn.executescript(query)
  52. def _is_rank(self, user, rank):
  53. """Return True if the given user has the given rank, else False."""
  54. try:
  55. for rule in self._users[rank]:
  56. if user in rule:
  57. return rule
  58. except KeyError:
  59. pass
  60. return False
  61. def _set_rank(self, user, rank):
  62. """Add a User to the database under a given rank."""
  63. query = "INSERT INTO users VALUES (?, ?, ?, ?)"
  64. with self._db_access_lock:
  65. with sqlite.connect(self._dbfile) as conn:
  66. conn.execute(query, (user.nick, user.ident, user.host, rank))
  67. try:
  68. self._users[rank].append(user)
  69. except KeyError:
  70. self._users[rank] = [user]
  71. return user
  72. def _del_rank(self, user, rank):
  73. """Remove a User from the database."""
  74. query = """DELETE FROM users WHERE user_nick = ? AND user_ident = ? AND
  75. user_host = ? AND user_rank = ?"""
  76. with self._db_access_lock:
  77. try:
  78. for rule in self._users[rank]:
  79. if user in rule:
  80. with sqlite.connect(self._dbfile) as conn:
  81. args = (user.nick, user.ident, user.host, rank)
  82. conn.execute(query, args)
  83. self._users[rank].remove(rule)
  84. return rule
  85. except KeyError:
  86. pass
  87. return None
  88. @property
  89. def users(self):
  90. """A dict of all users in the permissions database."""
  91. return self._users
  92. @property
  93. def attributes(self):
  94. """A dict of all attributes in the permissions database."""
  95. return self._attributes
  96. def load(self):
  97. """Load permissions from an existing database, or create a new one."""
  98. qry1 = "SELECT user_nick, user_ident, user_host, user_rank FROM users"
  99. qry2 = "SELECT attr_uid, attr_key, attr_value FROM attributes"
  100. self._users = {}
  101. with sqlite.connect(self._dbfile) as conn, self._db_access_lock:
  102. try:
  103. for nick, ident, host, rank in conn.execute(qry1):
  104. try:
  105. self._users[rank].append(_User(nick, ident, host))
  106. except KeyError:
  107. self._users[rank] = [_User(nick, ident, host)]
  108. for user, key, value in conn.execute(qry2):
  109. try:
  110. self._attributes[user][key] = value
  111. except KeyError:
  112. self._attributes[user] = {key: value}
  113. except sqlite.OperationalError:
  114. self._create(conn)
  115. def has_exact(self, rank, nick="*", ident="*", host="*"):
  116. """Return ``True`` if there is an exact match for this rule."""
  117. try:
  118. for usr in self._users[rank]:
  119. if nick != usr.nick or ident != usr.ident or host != usr.host:
  120. continue
  121. return usr
  122. except KeyError:
  123. pass
  124. return False
  125. def is_admin(self, data):
  126. """Return ``True`` if the given user is a bot admin, else ``False``."""
  127. user = _User(data.nick, data.ident, data.host)
  128. return self._is_rank(user, rank=self.ADMIN)
  129. def is_owner(self, data):
  130. """Return ``True`` if the given user is a bot owner, else ``False``."""
  131. user = _User(data.nick, data.ident, data.host)
  132. return self._is_rank(user, rank=self.OWNER)
  133. def add_admin(self, nick="*", ident="*", host="*"):
  134. """Add a nick/ident/host combo to the bot admins list."""
  135. return self._set_rank(_User(nick, ident, host), rank=self.ADMIN)
  136. def add_owner(self, nick="*", ident="*", host="*"):
  137. """Add a nick/ident/host combo to the bot owners list."""
  138. return self._set_rank(_User(nick, ident, host), rank=self.OWNER)
  139. def remove_admin(self, nick="*", ident="*", host="*"):
  140. """Remove a nick/ident/host combo to the bot admins list."""
  141. return self._del_rank(_User(nick, ident, host), rank=self.ADMIN)
  142. def remove_owner(self, nick="*", ident="*", host="*"):
  143. """Remove a nick/ident/host combo to the bot owners list."""
  144. return self._del_rank(_User(nick, ident, host), rank=self.OWNER)
  145. def has_attr(self, user, key):
  146. """Return ``True`` if a given user has a certain attribute, *key*."""
  147. return user in self._attributes and key in self._attributes[user]
  148. def get_attr(self, user, key):
  149. """Get the value of the attribute *key* of a given *user*.
  150. Raises :py:exc:`KeyError` if the *key* or *user* is not found.
  151. """
  152. return self._attributes[user][key]
  153. def set_attr(self, user, key, value):
  154. """Set the *value* of the attribute *key* of a given *user*."""
  155. query1 = """SELECT attr_value FROM attributes WHERE attr_uid = ?
  156. AND attr_key = ?"""
  157. query2 = "INSERT INTO attributes VALUES (?, ?, ?)"
  158. query3 = """UPDATE attributes SET attr_value = ? WHERE attr_uid = ?
  159. AND attr_key = ?"""
  160. with self._db_access_lock, sqlite.connect(self._dbfile) as conn:
  161. if conn.execute(query1, (user, key)).fetchone():
  162. conn.execute(query3, (value, user, key))
  163. else:
  164. conn.execute(query2, (user, key, value))
  165. try:
  166. self._attributes[user][key] = value
  167. except KeyError:
  168. self.attributes[user] = {key: value}
  169. def remove_attr(self, user, key):
  170. """Remove the attribute *key* of a given *user*."""
  171. query = "DELETE FROM attributes WHERE attr_uid = ? AND attr_key = ?"
  172. with self._db_access_lock, sqlite.connect(self._dbfile) as conn:
  173. conn.execute(query, (user, key))
  174. class _User(object):
  175. """A class that represents an IRC user for the purpose of testing rules."""
  176. def __init__(self, nick, ident, host):
  177. self.nick = nick
  178. self.ident = ident
  179. self.host = host
  180. def __repr__(self):
  181. """Return the canonical string representation of the User."""
  182. res = "_User(nick={0!r}, ident={1!r}, host={2!r})"
  183. return res.format(self.nick, self.ident, self.host)
  184. def __str__(self):
  185. """Return a nice string representation of the User."""
  186. return "{0}!{1}@{2}".format(self.nick, self.ident, self.host)
  187. def __contains__(self, user):
  188. if fnmatch(user.nick, self.nick):
  189. if fnmatch(user.ident, self.ident):
  190. if fnmatch(user.host, self.host):
  191. return True
  192. return False