Add files via upload
This commit is contained in:
commit
2d60683beb
2 changed files with 583 additions and 0 deletions
281
README.md
Normal file
281
README.md
Normal file
|
|
@ -0,0 +1,281 @@
|
||||||
|
## Nostr Diagon Alley protocol (for resilient marketplaces)
|
||||||
|
|
||||||
|
#### Original protocol https://github.com/lnbits/Diagon-Alley
|
||||||
|
|
||||||
|
> The concepts around resilience in Diagon Alley helped influence the creation of the NOSTR protocol, now we get to build Diagon Alley on NOSTR!
|
||||||
|
|
||||||
|
In Diagon Alley, `merchant` and `customer` communicate via NOSTR relays, so loss of money, product information, and reputation become far less likely if attacked.
|
||||||
|
|
||||||
|
A `merchant` and `customer` both have a NOSTR key-pair that are used to sign notes and subscribe to events.
|
||||||
|
|
||||||
|
#### For further information about NOSTR, see https://github.com/nostr-protocol/nostr
|
||||||
|
|
||||||
|
|
||||||
|
## Terms
|
||||||
|
|
||||||
|
* `merchant` - seller of products with NOSTR key-pair
|
||||||
|
* `customer` - buyer of products with NOSTR key-pair
|
||||||
|
* `product` - item for sale by the `merchant`
|
||||||
|
* `stall` - list of products controlled by `merchant` (a `merchant` can have multiple stalls)
|
||||||
|
* `marketplace` - clientside software for searching `stalls` and purchasing `products`
|
||||||
|
|
||||||
|
## Diagon Alley Clients
|
||||||
|
|
||||||
|
### Merchant admin
|
||||||
|
|
||||||
|
Where the `merchant` creates, updates and deletes `stalls` and `products`, as well as where they manage sales, payments and communication with `customers`.
|
||||||
|
|
||||||
|
The `merchant` admin software can be purely clientside, but for `convenience` and uptime, implementations will likely have a server listening for NOSTR events.
|
||||||
|
|
||||||
|
### Marketplace
|
||||||
|
|
||||||
|
`Marketplace` software should be entirely clientside, either as a stand-alone app, or as a purely frontend webpage. A `customer` subscribes to different merchant NOSTR public keys, and those `merchants` `stalls` and `products` become listed and searchable. The marketplace client is like any other ecommerce site, with basket and checkout. `Marketplaces` may also wish to include a `customer` support area for direct message communication with `merchants`.
|
||||||
|
|
||||||
|
## `Merchant` publishing/updating products (event)
|
||||||
|
|
||||||
|
NIP-01 https://github.com/nostr-protocol/nips/blob/master/01.md uses the basic NOSTR event type.
|
||||||
|
|
||||||
|
The `merchant` event that publishes and updates product lists
|
||||||
|
|
||||||
|
The below json goes in `content` of NIP-01.
|
||||||
|
|
||||||
|
Data from newer events should replace data from older events.
|
||||||
|
|
||||||
|
`action` types (used to indicate changes):
|
||||||
|
* `update` element has changed
|
||||||
|
* `delete` element should be deleted
|
||||||
|
* `suspend` element is suspended
|
||||||
|
* `unsuspend` element is unsuspended
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"name": <String, name of merchant>,
|
||||||
|
"description": <String, description of merchant>,
|
||||||
|
"currency": <Str, currency used>,
|
||||||
|
"action": <String, optional action>,
|
||||||
|
"shipping": [
|
||||||
|
{
|
||||||
|
"id": <String, UUID derived from stall ID>,
|
||||||
|
"zones": <String, CSV of countries/zones>,
|
||||||
|
"price": <int, cost>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": <String, UUID derived from stall ID>,
|
||||||
|
"zones": <String, CSV of countries/zones>,
|
||||||
|
"price": <int, cost>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": <String, UUID derived from stall ID>,
|
||||||
|
"zones": <String, CSV of countries/zones>,
|
||||||
|
"price": <int, cost>,
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"stalls": [
|
||||||
|
{
|
||||||
|
"id": <UUID derived from merchant public-key>,
|
||||||
|
"name": <String, stall name>,
|
||||||
|
"description": <String, stall description>,
|
||||||
|
"categories": <String, CSV of voluntary categories>,
|
||||||
|
"shipping": <String, CSV of shipping ids>,
|
||||||
|
"action": <String, optional action>,
|
||||||
|
"products": [
|
||||||
|
{
|
||||||
|
"id": <String, UUID derived from stall ID>,
|
||||||
|
"name": <String, name of product>,
|
||||||
|
"description": <String, product description>,
|
||||||
|
"categories": <String, CSV of voluntary categories>,
|
||||||
|
"amount": <Int, number of units>,
|
||||||
|
"price": <Int, cost per unit>,
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"id": <String, UUID derived from product ID>,
|
||||||
|
"name": <String, image name>,
|
||||||
|
"link": <String, URL or BASE64>
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"action": <String, optional action>,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": <String, UUID derived from stall ID>,
|
||||||
|
"name": <String, name of product>,
|
||||||
|
"description": <String, product description>,
|
||||||
|
"categories": <String, CSV of voluntary categories>,
|
||||||
|
"amount": <Int, number of units>,
|
||||||
|
"price": <Int, cost per unit>,
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"id": <String, UUID derived from product ID>,
|
||||||
|
"name": <String, image name>,
|
||||||
|
"link": <String, URL or BASE64>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": <String, UUID derived from product ID>,
|
||||||
|
"name": <String, image name>,
|
||||||
|
"link": <String, URL or BASE64>
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"action": <String, optional action>,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": <UUID derived from merchant public_key>,
|
||||||
|
"name": <String, stall name>,
|
||||||
|
"description": <String, stall description>,
|
||||||
|
"categories": <String, CSV of voluntary categories>,
|
||||||
|
"shipping": <String, CSV of shipping ids>,
|
||||||
|
"action": <String, optional action>,
|
||||||
|
"products": [
|
||||||
|
{
|
||||||
|
"id": <String, UUID derived from stall ID>,
|
||||||
|
"name": <String, name of product>,
|
||||||
|
"categories": <String, CSV of voluntary categories>,
|
||||||
|
"amount": <Int, number of units>,
|
||||||
|
"price": <Int, cost per unit>,
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"id": <String, UUID derived from product ID>,
|
||||||
|
"name": <String, image name>,
|
||||||
|
"link": <String, URL or BASE64>
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"action": <String, optional action>,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
As all elements are optional, an `update` `action` to a `product` `image`, may look as simple as:
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"stalls": [
|
||||||
|
{
|
||||||
|
"id": <UUID derived from merchant public-key>,
|
||||||
|
"products": [
|
||||||
|
{
|
||||||
|
"id": <String, UUID derived from stall ID>,
|
||||||
|
"images": [
|
||||||
|
{
|
||||||
|
"id": <String, UUID derived from product ID>,
|
||||||
|
"name": <String, image name>,
|
||||||
|
"link": <String, URL or BASE64>
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"action": <String, optional action>,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Checkout events
|
||||||
|
|
||||||
|
NIP-04 https://github.com/nostr-protocol/nips/blob/master/04.md, all checkout events are encrypted
|
||||||
|
|
||||||
|
The below json goes in `content` of NIP-04.
|
||||||
|
|
||||||
|
### Step 1: `customer` order (event)
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"id": <String, UUID derived from sum of product ids + timestamp>,
|
||||||
|
"name": <String, name of customer>,
|
||||||
|
"description": <String, description of customer>,
|
||||||
|
"address": <String, postal address>,
|
||||||
|
"message": <String, special request>,
|
||||||
|
"contact": [
|
||||||
|
"nostr": <String, NOSTR public key>,
|
||||||
|
"phone": <String, phone number>,
|
||||||
|
"email": <String, email address>
|
||||||
|
],
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"id": <String, product ID>,
|
||||||
|
"quantity": <String, stall name>,
|
||||||
|
"message": <String, special request>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": <String, product ID>,
|
||||||
|
"quantity": <String, stall name>,
|
||||||
|
"message": <String, special request>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": <String, product ID>,
|
||||||
|
"quantity": <String, stall name>,
|
||||||
|
"message": <String, special request>
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
Merchant should verify the sum of product ids + timestamp.
|
||||||
|
|
||||||
|
### Step 2: `merchant` request payment (event)
|
||||||
|
|
||||||
|
Sent back from the merchant for payment. Any payment option is valid that the merchant can check.
|
||||||
|
|
||||||
|
The below json goes in `content` of NIP-04.
|
||||||
|
|
||||||
|
`payment_options`/`type` include:
|
||||||
|
* `url` URL to a payment page, stripe, paypal, btcpayserver, etc
|
||||||
|
* `btc` onchain bitcoin address
|
||||||
|
* `ln` bitcoin lightning invoice
|
||||||
|
* `lnurl` bitcoin lnurl-pay
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"id": <String, UUID derived from sum of product ids + timestamp>,
|
||||||
|
"message": <String, message to customer>,
|
||||||
|
"payment_options": [
|
||||||
|
{
|
||||||
|
"type": <String, option type>,
|
||||||
|
"link": <String, url, btc address, ln invoice, etc>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": <String, option type>,
|
||||||
|
"link": <String, url, btc address, ln invoice, etc>
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": <String, option type>,
|
||||||
|
"link": <String, url, btc address, ln invoice, etc>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 3: `merchant` verify payment/shipped (event)
|
||||||
|
|
||||||
|
Once payment has been received and processed.
|
||||||
|
|
||||||
|
The below json goes in `content` of NIP-04.
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"id": <String, UUID derived from sum of product ids + timestamp>,
|
||||||
|
"message": <String, message to customer>,
|
||||||
|
"paid": <Bool, true/false has received payment>,
|
||||||
|
"shipped": <Bool, true/false has been shipped>,
|
||||||
|
}
|
||||||
|
|
||||||
|
```
|
||||||
|
|
||||||
|
## Customer support events
|
||||||
|
|
||||||
|
Customer support is handle over whatever communication method was specified. If communicationg via nostr, NIP-04 is used https://github.com/nostr-protocol/nips/blob/master/04.md.
|
||||||
|
|
||||||
|
## Additional
|
||||||
|
|
||||||
|
Standard data models can be found here <a href="models.json">here</a>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
302
migrations.py
Normal file
302
migrations.py
Normal file
|
|
@ -0,0 +1,302 @@
|
||||||
|
async def m001_initial(db):
|
||||||
|
"""
|
||||||
|
Initial Market settings table.
|
||||||
|
"""
|
||||||
|
await db.execute(
|
||||||
|
"""
|
||||||
|
CREATE TABLE market.settings (
|
||||||
|
"user" TEXT PRIMARY KEY,
|
||||||
|
currency TEXT DEFAULT 'sat',
|
||||||
|
fiat_base_multiplier INTEGER DEFAULT 1
|
||||||
|
);
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
"""
|
||||||
|
Initial stalls table.
|
||||||
|
"""
|
||||||
|
await db.execute(
|
||||||
|
"""
|
||||||
|
CREATE TABLE market.stalls (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
wallet TEXT NOT NULL,
|
||||||
|
name TEXT NOT NULL,
|
||||||
|
currency TEXT,
|
||||||
|
publickey TEXT,
|
||||||
|
relays TEXT,
|
||||||
|
shippingzones TEXT NOT NULL,
|
||||||
|
rating INTEGER DEFAULT 0
|
||||||
|
);
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
"""
|
||||||
|
Initial products table.
|
||||||
|
"""
|
||||||
|
await db.execute(
|
||||||
|
f"""
|
||||||
|
CREATE TABLE market.products (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
stall TEXT NOT NULL REFERENCES {db.references_schema}stalls (id) ON DELETE CASCADE,
|
||||||
|
product TEXT NOT NULL,
|
||||||
|
categories TEXT,
|
||||||
|
description TEXT,
|
||||||
|
image TEXT,
|
||||||
|
price INTEGER NOT NULL,
|
||||||
|
quantity INTEGER NOT NULL,
|
||||||
|
rating INTEGER DEFAULT 0
|
||||||
|
);
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
"""
|
||||||
|
Initial zones table.
|
||||||
|
"""
|
||||||
|
await db.execute(
|
||||||
|
"""
|
||||||
|
CREATE TABLE market.zones (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
"user" TEXT NOT NULL,
|
||||||
|
cost TEXT NOT NULL,
|
||||||
|
countries TEXT NOT NULL
|
||||||
|
);
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
"""
|
||||||
|
Initial orders table.
|
||||||
|
"""
|
||||||
|
await db.execute(
|
||||||
|
f"""
|
||||||
|
CREATE TABLE market.orders (
|
||||||
|
id {db.serial_primary_key},
|
||||||
|
wallet TEXT NOT NULL,
|
||||||
|
username TEXT,
|
||||||
|
pubkey TEXT,
|
||||||
|
shippingzone TEXT NOT NULL,
|
||||||
|
address TEXT NOT NULL,
|
||||||
|
email TEXT NOT NULL,
|
||||||
|
total INTEGER NOT NULL,
|
||||||
|
invoiceid TEXT NOT NULL,
|
||||||
|
paid BOOLEAN NOT NULL,
|
||||||
|
shipped BOOLEAN NOT NULL,
|
||||||
|
time TIMESTAMP NOT NULL DEFAULT """
|
||||||
|
+ db.timestamp_now
|
||||||
|
+ """
|
||||||
|
);
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
"""
|
||||||
|
Initial order details table.
|
||||||
|
"""
|
||||||
|
await db.execute(
|
||||||
|
f"""
|
||||||
|
CREATE TABLE market.order_details (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
order_id INTEGER NOT NULL REFERENCES {db.references_schema}orders (id) ON DELETE CASCADE,
|
||||||
|
product_id TEXT NOT NULL REFERENCES {db.references_schema}products (id) ON DELETE CASCADE,
|
||||||
|
quantity INTEGER NOT NULL
|
||||||
|
);
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
"""
|
||||||
|
Initial market table.
|
||||||
|
"""
|
||||||
|
await db.execute(
|
||||||
|
"""
|
||||||
|
CREATE TABLE market.markets (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
usr TEXT NOT NULL,
|
||||||
|
name TEXT
|
||||||
|
);
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
"""
|
||||||
|
Initial market stalls table.
|
||||||
|
"""
|
||||||
|
await db.execute(
|
||||||
|
f"""
|
||||||
|
CREATE TABLE market.market_stalls (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
marketid TEXT NOT NULL REFERENCES {db.references_schema}markets (id) ON DELETE CASCADE,
|
||||||
|
stallid TEXT NOT NULL REFERENCES {db.references_schema}stalls (id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
"""
|
||||||
|
Initial chat messages table.
|
||||||
|
"""
|
||||||
|
await db.execute(
|
||||||
|
f"""
|
||||||
|
CREATE TABLE market.messages (
|
||||||
|
id {db.serial_primary_key},
|
||||||
|
msg TEXT NOT NULL,
|
||||||
|
pubkey TEXT NOT NULL,
|
||||||
|
id_conversation TEXT NOT NULL,
|
||||||
|
timestamp TIMESTAMP NOT NULL DEFAULT """
|
||||||
|
+ db.timestamp_now
|
||||||
|
+ """
|
||||||
|
);
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
if db.type != "SQLITE":
|
||||||
|
"""
|
||||||
|
Create indexes for message fetching
|
||||||
|
"""
|
||||||
|
await db.execute(
|
||||||
|
"CREATE INDEX idx_messages_timestamp ON market.messages (timestamp DESC)"
|
||||||
|
)
|
||||||
|
await db.execute(
|
||||||
|
"CREATE INDEX idx_messages_conversations ON market.messages (id_conversation)"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def m002_add_custom_relays(db):
|
||||||
|
"""
|
||||||
|
Add custom relays to stores
|
||||||
|
"""
|
||||||
|
await db.execute("ALTER TABLE market.stalls ADD COLUMN crelays TEXT;")
|
||||||
|
|
||||||
|
|
||||||
|
async def m003_fiat_base_multiplier(db):
|
||||||
|
"""
|
||||||
|
Store the multiplier for fiat prices. We store the price in cents and
|
||||||
|
remember to multiply by 100 when we use it to convert to Dollars.
|
||||||
|
"""
|
||||||
|
await db.execute(
|
||||||
|
"ALTER TABLE market.stalls ADD COLUMN fiat_base_multiplier INTEGER DEFAULT 1;"
|
||||||
|
)
|
||||||
|
|
||||||
|
await db.execute(
|
||||||
|
"UPDATE market.stalls SET fiat_base_multiplier = 100 WHERE NOT currency = 'sat';"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
async def m004_add_privkey_to_stalls(db):
|
||||||
|
await db.execute("ALTER TABLE market.stalls ADD COLUMN privatekey TEXT")
|
||||||
|
|
||||||
|
|
||||||
|
async def m005_add_currency_to_zones(db):
|
||||||
|
await db.execute("ALTER TABLE market.zones ADD COLUMN stall TEXT")
|
||||||
|
await db.execute("ALTER TABLE market.zones ADD COLUMN currency TEXT DEFAULT 'sat'")
|
||||||
|
|
||||||
|
|
||||||
|
async def m006_delete_market_settings(db):
|
||||||
|
await db.execute("DROP TABLE market.settings")
|
||||||
|
|
||||||
|
|
||||||
|
async def m007_order_id_to_UUID(db):
|
||||||
|
"""
|
||||||
|
Migrate ID column type to string for UUIDs and migrate existing data
|
||||||
|
"""
|
||||||
|
|
||||||
|
await db.execute("ALTER TABLE market.orders RENAME TO orders_old")
|
||||||
|
await db.execute(
|
||||||
|
f"""
|
||||||
|
CREATE TABLE market.orders (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
wallet TEXT NOT NULL,
|
||||||
|
username TEXT,
|
||||||
|
pubkey TEXT,
|
||||||
|
shippingzone TEXT NOT NULL,
|
||||||
|
address TEXT NOT NULL,
|
||||||
|
email TEXT NOT NULL,
|
||||||
|
total INTEGER NOT NULL,
|
||||||
|
invoiceid TEXT NOT NULL,
|
||||||
|
paid BOOLEAN NOT NULL,
|
||||||
|
shipped BOOLEAN NOT NULL,
|
||||||
|
time TIMESTAMP NOT NULL DEFAULT """
|
||||||
|
+ db.timestamp_now
|
||||||
|
+ """
|
||||||
|
);
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
for row in [
|
||||||
|
list(row) for row in await db.fetchall("SELECT * FROM market.orders_old")
|
||||||
|
]:
|
||||||
|
await db.execute(
|
||||||
|
"""
|
||||||
|
INSERT INTO market.orders (
|
||||||
|
id,
|
||||||
|
wallet,
|
||||||
|
username,
|
||||||
|
pubkey,
|
||||||
|
shippingzone,
|
||||||
|
address,
|
||||||
|
email,
|
||||||
|
total,
|
||||||
|
invoiceid,
|
||||||
|
paid,
|
||||||
|
shipped,
|
||||||
|
time
|
||||||
|
)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
|
""",
|
||||||
|
(
|
||||||
|
str(row[0]),
|
||||||
|
row[1],
|
||||||
|
row[2],
|
||||||
|
row[3],
|
||||||
|
row[4],
|
||||||
|
row[5],
|
||||||
|
row[6],
|
||||||
|
row[7],
|
||||||
|
row[8],
|
||||||
|
row[9],
|
||||||
|
row[10],
|
||||||
|
int(row[11]),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
await db.execute("DROP TABLE market.orders_old")
|
||||||
|
|
||||||
|
|
||||||
|
async def m008_message_id_to_TEXT(db):
|
||||||
|
"""
|
||||||
|
Migrate ID column type to string for UUIDs and migrate existing data
|
||||||
|
"""
|
||||||
|
|
||||||
|
await db.execute("ALTER TABLE market.messages RENAME TO messages_old")
|
||||||
|
await db.execute(
|
||||||
|
f"""
|
||||||
|
CREATE TABLE market.messages (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
msg TEXT NOT NULL,
|
||||||
|
pubkey TEXT NOT NULL,
|
||||||
|
id_conversation TEXT NOT NULL,
|
||||||
|
timestamp TIMESTAMP NOT NULL DEFAULT """
|
||||||
|
+ db.timestamp_now
|
||||||
|
+ """
|
||||||
|
);
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
|
||||||
|
for row in [
|
||||||
|
list(row) for row in await db.fetchall("SELECT * FROM market.messages_old")
|
||||||
|
]:
|
||||||
|
await db.execute(
|
||||||
|
"""
|
||||||
|
INSERT INTO market.messages(
|
||||||
|
id,
|
||||||
|
msg,
|
||||||
|
pubkey,
|
||||||
|
id_conversation,
|
||||||
|
timestamp
|
||||||
|
) VALUES (?, ?, ?, ?, ?)
|
||||||
|
""",
|
||||||
|
(
|
||||||
|
str(row[0]),
|
||||||
|
row[1],
|
||||||
|
row[2],
|
||||||
|
row[3],
|
||||||
|
int(row[4]),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
await db.execute("DROP TABLE market.messages_old")
|
||||||
Loading…
Add table
Add a link
Reference in a new issue