feat: basic chat
This commit is contained in:
parent
b7c099c935
commit
240da5277f
8 changed files with 202 additions and 13 deletions
6
crud.py
6
crud.py
|
|
@ -418,10 +418,10 @@ async def create_direct_message(
|
||||||
dm_id = urlsafe_short_hash()
|
dm_id = urlsafe_short_hash()
|
||||||
await db.execute(
|
await db.execute(
|
||||||
f"""
|
f"""
|
||||||
INSERT INTO nostrmarket.direct_messages (merchant_id, id, event_id, message, public_key, incomming)
|
INSERT INTO nostrmarket.direct_messages (merchant_id, id, event_id, message, public_key, incoming)
|
||||||
VALUES (?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, ?)
|
||||||
""",
|
""",
|
||||||
(merchant_id, dm_id, dm.event_id, dm.message, dm.public_key, dm.incomming),
|
(merchant_id, dm_id, dm.event_id, dm.message, dm.public_key, dm.incoming),
|
||||||
)
|
)
|
||||||
|
|
||||||
msg = await get_direct_message(merchant_id, dm_id)
|
msg = await get_direct_message(merchant_id, dm_id)
|
||||||
|
|
@ -442,7 +442,7 @@ async def get_direct_message(merchant_id: str, dm_id: str) -> Optional[DirectMes
|
||||||
|
|
||||||
async def get_direct_messages(merchant_id: str, public_key: str) -> List[DirectMessage]:
|
async def get_direct_messages(merchant_id: str, public_key: str) -> List[DirectMessage]:
|
||||||
rows = await db.fetchall(
|
rows = await db.fetchall(
|
||||||
"SELECT * FROM nostrmarket.zones WHERE merchant_id = ? AND public_ley = ?",
|
"SELECT * FROM nostrmarket.direct_messages WHERE merchant_id = ? AND public_key = ? ORDER BY time DESC",
|
||||||
(merchant_id, public_key),
|
(merchant_id, public_key),
|
||||||
)
|
)
|
||||||
return [DirectMessage.from_row(row) for row in rows]
|
return [DirectMessage.from_row(row) for row in rows]
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@ async def m001_initial(db):
|
||||||
event_id TEXT,
|
event_id TEXT,
|
||||||
message TEXT NOT NULL,
|
message TEXT NOT NULL,
|
||||||
public_key TEXT NOT NULL,
|
public_key TEXT NOT NULL,
|
||||||
incomming BOOLEAN NOT NULL DEFAULT false,
|
incoming BOOLEAN NOT NULL DEFAULT false,
|
||||||
time TIMESTAMP NOT NULL DEFAULT {db.timestamp_now}
|
time TIMESTAMP NOT NULL DEFAULT {db.timestamp_now}
|
||||||
);
|
);
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -368,11 +368,11 @@ class PartialDirectMessage(BaseModel):
|
||||||
event_id: Optional[str]
|
event_id: Optional[str]
|
||||||
message: str
|
message: str
|
||||||
public_key: str
|
public_key: str
|
||||||
incomming: bool = False
|
incoming: bool = False
|
||||||
time: Optional[int]
|
time: Optional[int]
|
||||||
|
|
||||||
|
|
||||||
class DirectMessage(BaseModel):
|
class DirectMessage(PartialDirectMessage):
|
||||||
id: str
|
id: str
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
||||||
|
|
@ -1 +1,58 @@
|
||||||
<div>dmmms</div>
|
<div>
|
||||||
|
<q-card>
|
||||||
|
<q-card-section>
|
||||||
|
<h6 class="text-subtitle1 q-my-none">Messages</h6>
|
||||||
|
</q-card-section>
|
||||||
|
<q-card-section class="q-pa-none">
|
||||||
|
<q-separator></q-separator>
|
||||||
|
</q-card-section>
|
||||||
|
<q-card-section>
|
||||||
|
<!-- <q-select
|
||||||
|
v-model="customerKey"
|
||||||
|
:options="Object.keys(messages).map(k => ({label: `${k.slice(0, 25)}...`, value: k}))"
|
||||||
|
label="Customers"
|
||||||
|
@input="chatRoom(customerKey)"
|
||||||
|
emit-value
|
||||||
|
></q-select> -->
|
||||||
|
</q-card-section>
|
||||||
|
<q-card-section>
|
||||||
|
<div class="chat-container" ref="chatCard">
|
||||||
|
<div class="chat-box">
|
||||||
|
<div class="chat-messages" style="height: 45vh">
|
||||||
|
<q-chat-message
|
||||||
|
v-for="(dm, index) in messages"
|
||||||
|
:key="index"
|
||||||
|
:name="dm.incoming ? 'customer': 'me'"
|
||||||
|
:text="[dm.message]"
|
||||||
|
:sent="!dm.incoming"
|
||||||
|
:bg-color="dm.incoming ? 'white' : 'light-green-2'"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<q-card-section>
|
||||||
|
<q-form @submit="sendDirectMesage" class="full-width chat-input">
|
||||||
|
<q-input
|
||||||
|
ref="newMessage"
|
||||||
|
v-model="newMessage"
|
||||||
|
placeholder="Message"
|
||||||
|
class="full-width"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
>
|
||||||
|
<template>
|
||||||
|
<q-btn
|
||||||
|
round
|
||||||
|
dense
|
||||||
|
flat
|
||||||
|
type="submit"
|
||||||
|
icon="send"
|
||||||
|
color="primary"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
|
</q-form>
|
||||||
|
</q-card-section>
|
||||||
|
</div>
|
||||||
|
</q-card-section>
|
||||||
|
</q-card>
|
||||||
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,51 @@ async function directMessages(path) {
|
||||||
template,
|
template,
|
||||||
|
|
||||||
data: function () {
|
data: function () {
|
||||||
return {}
|
return {
|
||||||
|
activePublicKey:
|
||||||
|
'83d07a79496f4cbdc50ca585741a79a2df1fd938cfa449f0fccb0ab7352115dd',
|
||||||
|
messages: [],
|
||||||
|
newMessage: ''
|
||||||
|
}
|
||||||
},
|
},
|
||||||
methods: {},
|
methods: {
|
||||||
created: async function () {}
|
sendMessage: async function () {},
|
||||||
|
getDirectMessages: async function () {
|
||||||
|
if (!this.activePublicKey) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const {data} = await LNbits.api.request(
|
||||||
|
'GET',
|
||||||
|
'/nostrmarket/api/v1/message/' + this.activePublicKey,
|
||||||
|
this.inkey
|
||||||
|
)
|
||||||
|
this.messages = data
|
||||||
|
console.log('### this.messages', this.messages)
|
||||||
|
} catch (error) {
|
||||||
|
LNbits.utils.notifyApiError(error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
sendDirectMesage: async function () {
|
||||||
|
try {
|
||||||
|
const {data} = await LNbits.api.request(
|
||||||
|
'POST',
|
||||||
|
'/nostrmarket/api/v1/message',
|
||||||
|
this.adminkey,
|
||||||
|
{
|
||||||
|
message: this.newMessage,
|
||||||
|
public_key: this.activePublicKey
|
||||||
|
}
|
||||||
|
)
|
||||||
|
this.messages.push(data)
|
||||||
|
this.newMessage = ''
|
||||||
|
} catch (error) {
|
||||||
|
LNbits.utils.notifyApiError(error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
created: async function () {
|
||||||
|
await this.getDirectMessages()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
tasks.py
2
tasks.py
|
|
@ -149,7 +149,7 @@ async def handle_dirrect_message(
|
||||||
event_id=event_id,
|
event_id=event_id,
|
||||||
message=text_msg,
|
message=text_msg,
|
||||||
public_key=from_pubkey,
|
public_key=from_pubkey,
|
||||||
incomming=True,
|
incoming=True,
|
||||||
)
|
)
|
||||||
await create_direct_message(merchant_id, dm)
|
await create_direct_message(merchant_id, dm)
|
||||||
return None
|
return None
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,6 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-12 col-md-5 q-gutter-y-md">
|
<div class="col-12 col-md-5 q-gutter-y-md">
|
||||||
<!-- <div class="row q-col-gutter-md"> -->
|
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<q-card>
|
<q-card>
|
||||||
<q-card-section>
|
<q-card-section>
|
||||||
|
|
@ -115,7 +114,13 @@
|
||||||
</q-card-section>
|
</q-card-section>
|
||||||
</q-card>
|
</q-card>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12">x111</div>
|
<div class="col-12">
|
||||||
|
<direct-messages
|
||||||
|
:inkey="g.user.wallets[0].inkey"
|
||||||
|
:adminkey="g.user.wallets[0].adminkey"
|
||||||
|
>
|
||||||
|
</direct-messages>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -146,6 +151,43 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock%}{% block scripts %} {{ window_vars(user) }}
|
{% endblock%}{% block scripts %} {{ window_vars(user) }}
|
||||||
|
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.q-field__native span {
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
.chat-container {
|
||||||
|
position: relative;
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: 1fr auto;
|
||||||
|
/*height: calc(100vh - 200px);*/
|
||||||
|
height: 50vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-box {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column-reverse;
|
||||||
|
padding: 1rem;
|
||||||
|
overflow-y: auto;
|
||||||
|
margin-left: auto;
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-messages {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-input {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: end;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
<!-- todo: serve locally -->
|
<!-- todo: serve locally -->
|
||||||
<script src="https://unpkg.com/nostr-tools/lib/nostr.bundle.js"></script>
|
<script src="https://unpkg.com/nostr-tools/lib/nostr.bundle.js"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/pica@6.1.1/dist/pica.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/pica@6.1.1/dist/pica.min.js"></script>
|
||||||
|
|
@ -159,4 +201,5 @@
|
||||||
<script src="{{ url_for('nostrmarket_static', path='components/direct-messages/direct-messages.js') }}"></script>
|
<script src="{{ url_for('nostrmarket_static', path='components/direct-messages/direct-messages.js') }}"></script>
|
||||||
<script src="{{ url_for('nostrmarket_static', path='js/index.js') }}"></script>
|
<script src="{{ url_for('nostrmarket_static', path='js/index.js') }}"></script>
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
47
views_api.py
47
views_api.py
|
|
@ -18,6 +18,7 @@ from lnbits.utils.exchange_rates import currencies
|
||||||
|
|
||||||
from . import nostrmarket_ext, scheduled_tasks
|
from . import nostrmarket_ext, scheduled_tasks
|
||||||
from .crud import (
|
from .crud import (
|
||||||
|
create_direct_message,
|
||||||
create_merchant,
|
create_merchant,
|
||||||
create_order,
|
create_order,
|
||||||
create_product,
|
create_product,
|
||||||
|
|
@ -26,6 +27,7 @@ from .crud import (
|
||||||
delete_product,
|
delete_product,
|
||||||
delete_stall,
|
delete_stall,
|
||||||
delete_zone,
|
delete_zone,
|
||||||
|
get_direct_messages,
|
||||||
get_merchant_for_user,
|
get_merchant_for_user,
|
||||||
get_order,
|
get_order,
|
||||||
get_order_by_event_id,
|
get_order_by_event_id,
|
||||||
|
|
@ -45,11 +47,13 @@ from .crud import (
|
||||||
update_zone,
|
update_zone,
|
||||||
)
|
)
|
||||||
from .models import (
|
from .models import (
|
||||||
|
DirectMessage,
|
||||||
Merchant,
|
Merchant,
|
||||||
Nostrable,
|
Nostrable,
|
||||||
Order,
|
Order,
|
||||||
OrderExtra,
|
OrderExtra,
|
||||||
OrderStatusUpdate,
|
OrderStatusUpdate,
|
||||||
|
PartialDirectMessage,
|
||||||
PartialMerchant,
|
PartialMerchant,
|
||||||
PartialOrder,
|
PartialOrder,
|
||||||
PartialProduct,
|
PartialProduct,
|
||||||
|
|
@ -576,6 +580,49 @@ async def api_update_order_status(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
######################################## DIRECT MESSAGES ########################################
|
||||||
|
|
||||||
|
|
||||||
|
@nostrmarket_ext.get("/api/v1/message/{public_key}")
|
||||||
|
async def api_get_messages(
|
||||||
|
public_key: str, wallet: WalletTypeInfo = Depends(get_key_type)
|
||||||
|
) -> List[DirectMessage]:
|
||||||
|
try:
|
||||||
|
merchant = await get_merchant_for_user(wallet.wallet.user)
|
||||||
|
assert merchant, f"Merchant cannot be found"
|
||||||
|
|
||||||
|
messages = await get_direct_messages(merchant.id, public_key)
|
||||||
|
return messages
|
||||||
|
except Exception as ex:
|
||||||
|
logger.warning(ex)
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
detail="Cannot get zone",
|
||||||
|
)
|
||||||
|
|
||||||
|
@nostrmarket_ext.post("/api/v1/message")
|
||||||
|
async def api_create_message(
|
||||||
|
data: PartialDirectMessage, wallet: WalletTypeInfo = Depends(require_admin_key)
|
||||||
|
) -> DirectMessage:
|
||||||
|
try:
|
||||||
|
merchant = await get_merchant_for_user(wallet.wallet.user)
|
||||||
|
assert merchant, f"Merchant cannot be found"
|
||||||
|
|
||||||
|
dm_event = merchant.build_dm_event(data.message, data.public_key)
|
||||||
|
data.event_id = dm_event.id
|
||||||
|
|
||||||
|
dm = await create_direct_message(merchant.id, data)
|
||||||
|
await publish_nostr_event(dm_event)
|
||||||
|
|
||||||
|
return dm
|
||||||
|
except Exception as ex:
|
||||||
|
logger.warning(ex)
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=HTTPStatus.INTERNAL_SERVER_ERROR,
|
||||||
|
detail="Cannot create message",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
######################################## OTHER ########################################
|
######################################## OTHER ########################################
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue