diff --git a/lnbits/extensions/discordbot/Pipfile b/lnbits/extensions/discordbot/Pipfile
new file mode 100644
index 00000000..d5820662
--- /dev/null
+++ b/lnbits/extensions/discordbot/Pipfile
@@ -0,0 +1,11 @@
+[[source]]
+url = "https://pypi.python.org/simple"
+verify_ssl = true
+name = "pypi"
+
+[packages]
+
+[dev-packages]
+
+[requires]
+python_version = "3.9"
diff --git a/lnbits/extensions/discordbot/README.md b/lnbits/extensions/discordbot/README.md
new file mode 100644
index 00000000..a1408317
--- /dev/null
+++ b/lnbits/extensions/discordbot/README.md
@@ -0,0 +1,34 @@
+# Discord Bot
+
+## Provide LNbits wallets for all your Discord users
+
+_This extension is a modifed version of LNbits [User Manager](../usermanager/README.md)_
+
+The intended usage of this extension is to connect it to a specifically designed [Discord Bot](https://github.com/chrislennon/lnbits-discord-bot) leveraging LNbits as a community based lightning node.
+
+## Setup
+This bot can target [lnbits.com](https://lnbits.com) or a self hosted instance.
+
+To setup and run the bot instructions are located [here](https://github.com/chrislennon/lnbits-discord-bot#installation)
+
+## Usage
+This bot will allow users to interact with it in the following ways [full command list](https://github.com/chrislennon/lnbits-discord-bot#commands):
+
+`/create` Will create a wallet for the Discord user
+ - (currently limiting 1 Discord user == 1 LNbits user == 1 user wallet)
+
+
+
+`/balance` Will show the balance of the users wallet.
+
+
+
+`/tip @user [amount]` Will sent money from one user to another
+ - If the recieving user does not have a wallet, one will be created for them
+ - The receiving user will receive a direct message from the bot with a link to their wallet
+
+
+
+`/payme [amount] [description]` Will open an invoice that can be paid by any user
+
+
diff --git a/lnbits/extensions/discordbot/__init__.py b/lnbits/extensions/discordbot/__init__.py
new file mode 100644
index 00000000..ff60dd62
--- /dev/null
+++ b/lnbits/extensions/discordbot/__init__.py
@@ -0,0 +1,25 @@
+from fastapi import APIRouter
+from fastapi.staticfiles import StaticFiles
+
+from lnbits.db import Database
+from lnbits.helpers import template_renderer
+
+db = Database("ext_discordbot")
+
+discordbot_static_files = [
+ {
+ "path": "/discordbot/static",
+ "app": StaticFiles(directory="lnbits/extensions/discordbot/static"),
+ "name": "discordbot_static",
+ }
+]
+
+discordbot_ext: APIRouter = APIRouter(prefix="/discordbot", tags=["discordbot"])
+
+
+def discordbot_renderer():
+ return template_renderer(["lnbits/extensions/discordbot/templates"])
+
+
+from .views import * # noqa
+from .views_api import * # noqa
diff --git a/lnbits/extensions/discordbot/config.json b/lnbits/extensions/discordbot/config.json
new file mode 100644
index 00000000..eb674122
--- /dev/null
+++ b/lnbits/extensions/discordbot/config.json
@@ -0,0 +1,6 @@
+{
+ "name": "Discord Bot",
+ "short_description": "Generate users and wallets",
+ "icon": "person_add",
+ "contributors": ["bitcoingamer21"]
+}
diff --git a/lnbits/extensions/discordbot/crud.py b/lnbits/extensions/discordbot/crud.py
new file mode 100644
index 00000000..5661fcb4
--- /dev/null
+++ b/lnbits/extensions/discordbot/crud.py
@@ -0,0 +1,123 @@
+from typing import List, Optional
+
+from lnbits.core.crud import (
+ create_account,
+ create_wallet,
+ delete_wallet,
+ get_payments,
+ get_user,
+)
+from lnbits.core.models import Payment
+
+from . import db
+from .models import CreateUserData, Users, Wallets
+
+### Users
+
+
+async def create_discordbot_user(data: CreateUserData) -> Users:
+ account = await create_account()
+ user = await get_user(account.id)
+ assert user, "Newly created user couldn't be retrieved"
+
+ wallet = await create_wallet(user_id=user.id, wallet_name=data.wallet_name)
+
+ await db.execute(
+ """
+ INSERT INTO discordbot.users (id, name, admin, discord_id)
+ VALUES (?, ?, ?, ?)
+ """,
+ (user.id, data.user_name, data.admin_id, data.discord_id),
+ )
+
+ await db.execute(
+ """
+ INSERT INTO discordbot.wallets (id, admin, name, "user", adminkey, inkey)
+ VALUES (?, ?, ?, ?, ?, ?)
+ """,
+ (
+ wallet.id,
+ data.admin_id,
+ data.wallet_name,
+ user.id,
+ wallet.adminkey,
+ wallet.inkey,
+ ),
+ )
+
+ user_created = await get_discordbot_user(user.id)
+ assert user_created, "Newly created user couldn't be retrieved"
+ return user_created
+
+
+async def get_discordbot_user(user_id: str) -> Optional[Users]:
+ row = await db.fetchone("SELECT * FROM discordbot.users WHERE id = ?", (user_id,))
+ return Users(**row) if row else None
+
+
+async def get_discordbot_users(user_id: str) -> List[Users]:
+ rows = await db.fetchall(
+ "SELECT * FROM discordbot.users WHERE admin = ?", (user_id,)
+ )
+
+ return [Users(**row) for row in rows]
+
+
+async def delete_discordbot_user(user_id: str) -> None:
+ wallets = await get_discordbot_wallets(user_id)
+ for wallet in wallets:
+ await delete_wallet(user_id=user_id, wallet_id=wallet.id)
+
+ await db.execute("DELETE FROM discordbot.users WHERE id = ?", (user_id,))
+ await db.execute("""DELETE FROM discordbot.wallets WHERE "user" = ?""", (user_id,))
+
+
+### Wallets
+
+
+async def create_discordbot_wallet(
+ user_id: str, wallet_name: str, admin_id: str
+) -> Wallets:
+ wallet = await create_wallet(user_id=user_id, wallet_name=wallet_name)
+ await db.execute(
+ """
+ INSERT INTO discordbot.wallets (id, admin, name, "user", adminkey, inkey)
+ VALUES (?, ?, ?, ?, ?, ?)
+ """,
+ (wallet.id, admin_id, wallet_name, user_id, wallet.adminkey, wallet.inkey),
+ )
+ wallet_created = await get_discordbot_wallet(wallet.id)
+ assert wallet_created, "Newly created wallet couldn't be retrieved"
+ return wallet_created
+
+
+async def get_discordbot_wallet(wallet_id: str) -> Optional[Wallets]:
+ row = await db.fetchone(
+ "SELECT * FROM discordbot.wallets WHERE id = ?", (wallet_id,)
+ )
+ return Wallets(**row) if row else None
+
+
+async def get_discordbot_wallets(admin_id: str) -> Optional[Wallets]:
+ rows = await db.fetchall(
+ "SELECT * FROM discordbot.wallets WHERE admin = ?", (admin_id,)
+ )
+ return [Wallets(**row) for row in rows]
+
+
+async def get_discordbot_users_wallets(user_id: str) -> Optional[Wallets]:
+ rows = await db.fetchall(
+ """SELECT * FROM discordbot.wallets WHERE "user" = ?""", (user_id,)
+ )
+ return [Wallets(**row) for row in rows]
+
+
+async def get_discordbot_wallet_transactions(wallet_id: str) -> Optional[Payment]:
+ return await get_payments(
+ wallet_id=wallet_id, complete=True, pending=False, outgoing=True, incoming=True
+ )
+
+
+async def delete_discordbot_wallet(wallet_id: str, user_id: str) -> None:
+ await delete_wallet(user_id=user_id, wallet_id=wallet_id)
+ await db.execute("DELETE FROM discordbot.wallets WHERE id = ?", (wallet_id,))
diff --git a/lnbits/extensions/discordbot/migrations.py b/lnbits/extensions/discordbot/migrations.py
new file mode 100644
index 00000000..ababfd7a
--- /dev/null
+++ b/lnbits/extensions/discordbot/migrations.py
@@ -0,0 +1,30 @@
+async def m001_initial(db):
+ """
+ Initial users table.
+ """
+ await db.execute(
+ """
+ CREATE TABLE discordbot.users (
+ id TEXT PRIMARY KEY,
+ name TEXT NOT NULL,
+ admin TEXT NOT NULL,
+ discord_id TEXT
+ );
+ """
+ )
+
+ """
+ Initial wallets table.
+ """
+ await db.execute(
+ """
+ CREATE TABLE discordbot.wallets (
+ id TEXT PRIMARY KEY,
+ admin TEXT NOT NULL,
+ name TEXT NOT NULL,
+ "user" TEXT NOT NULL,
+ adminkey TEXT NOT NULL,
+ inkey TEXT NOT NULL
+ );
+ """
+ )
diff --git a/lnbits/extensions/discordbot/models.py b/lnbits/extensions/discordbot/models.py
new file mode 100644
index 00000000..4be367f8
--- /dev/null
+++ b/lnbits/extensions/discordbot/models.py
@@ -0,0 +1,36 @@
+from sqlite3 import Row
+
+from fastapi.param_functions import Query
+from pydantic import BaseModel
+from typing import Optional
+
+
+class CreateUserData(BaseModel):
+ user_name: str = Query(...)
+ wallet_name: str = Query(...)
+ admin_id: str = Query(...)
+ discord_id: str = Query("")
+
+class CreateUserWallet(BaseModel):
+ user_id: str = Query(...)
+ wallet_name: str = Query(...)
+ admin_id: str = Query(...)
+
+
+class Users(BaseModel):
+ id: str
+ name: str
+ admin: str
+ discord_id: str
+
+class Wallets(BaseModel):
+ id: str
+ admin: str
+ name: str
+ user: str
+ adminkey: str
+ inkey: str
+
+ @classmethod
+ def from_row(cls, row: Row) -> "Wallets":
+ return cls(**dict(row))
diff --git a/lnbits/extensions/discordbot/static/stack.png b/lnbits/extensions/discordbot/static/stack.png
new file mode 100644
index 00000000..3b987db1
Binary files /dev/null and b/lnbits/extensions/discordbot/static/stack.png differ
diff --git a/lnbits/extensions/discordbot/templates/discordbot/_api_docs.html b/lnbits/extensions/discordbot/templates/discordbot/_api_docs.html
new file mode 100644
index 00000000..40fcfb12
--- /dev/null
+++ b/lnbits/extensions/discordbot/templates/discordbot/_api_docs.html
@@ -0,0 +1,260 @@
+
+ Connect your LNbits instance to a Discord Bot leveraging LNbits as a community based lightning node.
+ Discord Bot: Connect Discord users to LNbits.
+
+
+
+ Created by, Chris Lennon
+
+ Based on User Manager, by Ben Arc
+ GET
+ /discordbot/api/v1/users
+ Body (application/json)
+
+ Returns 201 CREATED (application/json)
+
+ JSON list of users
+ Curl example
+ curl -X GET {{ request.base_url }}discordbot/api/v1/users -H
+ "X-Api-Key: {{ user.wallets[0].inkey }}"
+
+ GET
+ /discordbot/api/v1/users/<user_id>
+ Body (application/json)
+
+ Returns 201 CREATED (application/json)
+
+ JSON list of users
+ Curl example
+ curl -X GET {{ request.base_url
+ }}discordbot/api/v1/users/<user_id> -H "X-Api-Key: {{
+ user.wallets[0].inkey }}"
+
+ GET
+ /discordbot/api/v1/wallets/<user_id>
+ Headers
+ {"X-Api-Key": <string>}
+ Body (application/json)
+
+ Returns 201 CREATED (application/json)
+
+ JSON wallet data
+ Curl example
+ curl -X GET {{ request.base_url
+ }}discordbot/api/v1/wallets/<user_id> -H "X-Api-Key: {{
+ user.wallets[0].inkey }}"
+
+ GET
+ /discordbot/api/v1/wallets<wallet_id>
+ Headers
+ {"X-Api-Key": <string>}
+ Body (application/json)
+
+ Returns 201 CREATED (application/json)
+
+ JSON a wallets transactions
+ Curl example
+ curl -X GET {{ request.base_url
+ }}discordbot/api/v1/wallets<wallet_id> -H "X-Api-Key: {{
+ user.wallets[0].inkey }}"
+
+ POST
+ /discordbot/api/v1/users
+ Headers
+ {"X-Api-Key": <string>, "Content-type":
+ "application/json"}
+
+ Body (application/json) - "admin_id" is a YOUR user ID
+
+ {"admin_id": <string>, "user_name": <string>,
+ "wallet_name": <string>,"discord_id": <string>}
+
+ Returns 201 CREATED (application/json)
+
+ {"id": <string>, "name": <string>, "admin":
+ <string>, "discord_id": <string>}
+ Curl example
+ curl -X POST {{ request.base_url }}discordbot/api/v1/users -d
+ '{"admin_id": "{{ user.id }}", "wallet_name": <string>,
+ "user_name": <string>, "discord_id": <string>}' -H "X-Api-Key: {{
+ user.wallets[0].inkey }}" -H "Content-type: application/json"
+
+ POST
+ /discordbot/api/v1/wallets
+ Headers
+ {"X-Api-Key": <string>, "Content-type":
+ "application/json"}
+
+ Body (application/json) - "admin_id" is a YOUR user ID
+
+ {"user_id": <string>, "wallet_name": <string>,
+ "admin_id": <string>}
+
+ Returns 201 CREATED (application/json)
+
+ {"id": <string>, "admin": <string>, "name":
+ <string>, "user": <string>, "adminkey": <string>,
+ "inkey": <string>}
+ Curl example
+ curl -X POST {{ request.base_url }}discordbot/api/v1/wallets -d
+ '{"user_id": <string>, "wallet_name": <string>,
+ "admin_id": "{{ user.id }}"}' -H "X-Api-Key: {{ user.wallets[0].inkey
+ }}" -H "Content-type: application/json"
+
+ DELETE
+ /discordbot/api/v1/users/<user_id>
+ Headers
+ {"X-Api-Key": <string>}
+ Curl example
+ curl -X DELETE {{ request.base_url
+ }}discordbot/api/v1/users/<user_id> -H "X-Api-Key: {{
+ user.wallets[0].inkey }}"
+
+ DELETE
+ /discordbot/api/v1/wallets/<wallet_id>
+ Headers
+ {"X-Api-Key": <string>}
+ Curl example
+ curl -X DELETE {{ request.base_url
+ }}discordbot/api/v1/wallets/<wallet_id> -H "X-Api-Key: {{
+ user.wallets[0].inkey }}"
+
+ POST
+ /discordbot/api/v1/extensions
+ Headers
+ {"X-Api-Key": <string>}
+ Curl example
+ curl -X POST {{ request.base_url }}discordbot/api/v1/extensions -d
+ '{"userid": <string>, "extension": <string>, "active":
+ <integer>}' -H "X-Api-Key: {{ user.wallets[0].inkey }}" -H
+ "Content-type: application/json"
+
+
