mirror of
https://github.com/hcengineering/platform.git
synced 2025-04-14 04:08:19 +00:00
Telegram attachments (#1127)
Signed-off-by: Denis Bykhov <80476319+BykhovDenis@users.noreply.github.com>
This commit is contained in:
parent
173c062369
commit
98bdcfce68
@ -61,6 +61,7 @@
|
||||
"@anticrm/server-core": "~0.6.1",
|
||||
"@anticrm/server-token": "~0.6.0",
|
||||
"@anticrm/model-attachment": "~0.6.0",
|
||||
"@anticrm/model-contact": "~0.6.0",
|
||||
"@anticrm/mongo": "~0.6.0",
|
||||
"@anticrm/dev-storage": "~0.6.0",
|
||||
"@anticrm/server-attachment": "~0.6.0",
|
||||
|
@ -199,9 +199,25 @@ program
|
||||
})
|
||||
|
||||
program
|
||||
.command('clear-telegram-history')
|
||||
.command('clear-telegram-history <workspace>')
|
||||
.description('clear telegram history')
|
||||
.option('-w, --workspace <workspace>', 'target workspace')
|
||||
.action(async (workspace: string, cmd) => {
|
||||
return await withDatabase(mongodbUri, async (db) => {
|
||||
const telegramDB = process.env.TELEGRAM_DATABASE
|
||||
if (telegramDB === undefined) {
|
||||
console.error('please provide TELEGRAM_DATABASE.')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
console.log(`clearing ${workspace} history:`)
|
||||
await clearTelegramHistory(mongodbUri, workspace, telegramDB, minio)
|
||||
})
|
||||
})
|
||||
|
||||
program
|
||||
.command('clear-telegram-all-history')
|
||||
.description('clear telegram history')
|
||||
.action(async (cmd) => {
|
||||
return await withDatabase(mongodbUri, async (db) => {
|
||||
const telegramDB = process.env.TELEGRAM_DATABASE
|
||||
@ -211,12 +227,10 @@ program
|
||||
}
|
||||
|
||||
const workspaces = await listWorkspaces(db)
|
||||
const targetWorkspaces =
|
||||
cmd.workspace !== undefined ? workspaces.filter((x) => x.workspace === cmd.workspace) : workspaces
|
||||
|
||||
for (const w of targetWorkspaces) {
|
||||
for (const w of workspaces) {
|
||||
console.log(`clearing ${w.workspace} history:`)
|
||||
await clearTelegramHistory(mongodbUri, w.workspace, telegramDB)
|
||||
await clearTelegramHistory(mongodbUri, w.workspace, telegramDB, minio)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
@ -14,24 +14,60 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { MongoClient } from 'mongodb'
|
||||
|
||||
import { DOMAIN_TX } from '@anticrm/core'
|
||||
import { DOMAIN_TX, Ref } from '@anticrm/core'
|
||||
import { DOMAIN_ATTACHMENT } from '@anticrm/model-attachment'
|
||||
import contact, { DOMAIN_CHANNEL } from '@anticrm/model-contact'
|
||||
import { DOMAIN_TELEGRAM } from '@anticrm/model-telegram'
|
||||
import telegram from '@anticrm/telegram'
|
||||
import telegram, { SharedTelegramMessage, SharedTelegramMessages } from '@anticrm/telegram'
|
||||
import { Client } from 'minio'
|
||||
import { MongoClient } from 'mongodb'
|
||||
|
||||
const LastMessages = 'last-msgs'
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export async function clearTelegramHistory (mongoUrl: string, workspace: string, tgDb: string): Promise<void> {
|
||||
export async function clearTelegramHistory (
|
||||
mongoUrl: string,
|
||||
workspace: string,
|
||||
tgDb: string,
|
||||
minio: Client
|
||||
): Promise<void> {
|
||||
const client = new MongoClient(mongoUrl)
|
||||
try {
|
||||
await client.connect()
|
||||
const workspaceDB = client.db(workspace)
|
||||
const telegramDB = client.db(tgDb)
|
||||
|
||||
const sharedMessages = await workspaceDB
|
||||
.collection(DOMAIN_TELEGRAM)
|
||||
.find<SharedTelegramMessages>({
|
||||
_class: telegram.class.SharedMessages
|
||||
})
|
||||
.toArray()
|
||||
const sharedIds: Ref<SharedTelegramMessage>[] = []
|
||||
for (const sharedMessage of sharedMessages) {
|
||||
for (const message of sharedMessage.messages) {
|
||||
sharedIds.push(message._id)
|
||||
}
|
||||
}
|
||||
const files = await workspaceDB
|
||||
.collection(DOMAIN_ATTACHMENT)
|
||||
.find(
|
||||
{
|
||||
attachedToClass: telegram.class.Message,
|
||||
attachedTo: { $nin: sharedIds }
|
||||
},
|
||||
{
|
||||
projection: {
|
||||
file: 1
|
||||
}
|
||||
}
|
||||
)
|
||||
.toArray()
|
||||
|
||||
const attachments = files.map((file) => file.file)
|
||||
|
||||
console.log('clearing txes and messages...')
|
||||
await Promise.all([
|
||||
workspaceDB.collection(DOMAIN_TX).deleteMany({
|
||||
@ -39,7 +75,21 @@ export async function clearTelegramHistory (mongoUrl: string, workspace: string,
|
||||
}),
|
||||
workspaceDB.collection(DOMAIN_TELEGRAM).deleteMany({
|
||||
_class: telegram.class.Message
|
||||
})
|
||||
}),
|
||||
workspaceDB.collection(DOMAIN_CHANNEL).updateMany(
|
||||
{
|
||||
provider: contact.channelProvider.Telegram
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
items: 0
|
||||
}
|
||||
}
|
||||
),
|
||||
workspaceDB.collection(DOMAIN_ATTACHMENT).deleteMany({
|
||||
attachedToClass: telegram.class.Message
|
||||
}),
|
||||
minio.removeObjects(workspace, Array.from(attachments))
|
||||
])
|
||||
|
||||
console.log('clearing telegram service data...')
|
||||
|
@ -30,6 +30,7 @@
|
||||
"@anticrm/core": "~0.6.0",
|
||||
"@anticrm/platform": "~0.6.5",
|
||||
"@anticrm/model-core": "~0.6.0",
|
||||
"@anticrm/model-attachment": "~0.6.0",
|
||||
"@anticrm/model-contact": "~0.6.1",
|
||||
"@anticrm/telegram": "~0.6.0",
|
||||
"@anticrm/telegram-resources": "~0.6.0",
|
||||
|
@ -14,14 +14,20 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
import { Builder, Model, TypeString, TypeBoolean, Prop, ArrOf, Index } from '@anticrm/model'
|
||||
import { Builder, Model, TypeString, TypeBoolean, Prop, ArrOf, Index, Collection } from '@anticrm/model'
|
||||
import core, { TAttachedDoc } from '@anticrm/model-core'
|
||||
import contact from '@anticrm/model-contact'
|
||||
import telegram from './plugin'
|
||||
import type { TelegramMessage, SharedTelegramMessage, SharedTelegramMessages } from '@anticrm/telegram'
|
||||
import type {
|
||||
TelegramMessage,
|
||||
NewTelegramMessage,
|
||||
SharedTelegramMessage,
|
||||
SharedTelegramMessages
|
||||
} from '@anticrm/telegram'
|
||||
import { Domain, IndexKind, Type } from '@anticrm/core'
|
||||
import setting from '@anticrm/setting'
|
||||
import activity from '@anticrm/activity'
|
||||
import attachment from '@anticrm/model-attachment'
|
||||
|
||||
export const DOMAIN_TELEGRAM = 'telegram' as Domain
|
||||
|
||||
@ -37,6 +43,22 @@ export class TTelegramMessage extends TAttachedDoc implements TelegramMessage {
|
||||
|
||||
@Prop(TypeBoolean(), telegram.string.Incoming)
|
||||
incoming!: boolean
|
||||
|
||||
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments)
|
||||
attachments?: number
|
||||
}
|
||||
|
||||
@Model(telegram.class.NewMessage, core.class.AttachedDoc, DOMAIN_TELEGRAM)
|
||||
export class TNewTelegramMessage extends TAttachedDoc implements NewTelegramMessage {
|
||||
@Prop(TypeString(), telegram.string.Content)
|
||||
@Index(IndexKind.FullText)
|
||||
content!: string
|
||||
|
||||
@Prop(TypeString(), telegram.string.Status)
|
||||
status!: 'new' | 'sent'
|
||||
|
||||
@Prop(Collection(attachment.class.Attachment), attachment.string.Attachments)
|
||||
attachments?: number
|
||||
}
|
||||
|
||||
@Model(telegram.class.SharedMessages, core.class.AttachedDoc, DOMAIN_TELEGRAM)
|
||||
@ -46,7 +68,7 @@ export class TSharedTelegramMessages extends TAttachedDoc implements SharedTeleg
|
||||
}
|
||||
|
||||
export function createModel (builder: Builder): void {
|
||||
builder.createModel(TTelegramMessage, TSharedTelegramMessages)
|
||||
builder.createModel(TTelegramMessage, TSharedTelegramMessages, TNewTelegramMessage)
|
||||
|
||||
builder.createDoc(
|
||||
contact.class.ChannelProvider,
|
||||
|
@ -29,7 +29,8 @@ export default mergeIds(telegramId, telegram, {
|
||||
Incoming: '' as IntlString,
|
||||
Messages: '' as IntlString,
|
||||
Telegram: '' as IntlString,
|
||||
TelegramIntegrationDesc: '' as IntlString
|
||||
TelegramIntegrationDesc: '' as IntlString,
|
||||
Status: '' as IntlString
|
||||
},
|
||||
ids: {
|
||||
TxSharedCreate: '' as Ref<TxViewlet>
|
||||
|
@ -20,6 +20,7 @@
|
||||
"Incoming": "Incoming",
|
||||
"Messages": "Messages",
|
||||
"Telegram": "Telegram",
|
||||
"TelegramIntegrationDesc": "Use telegram integration"
|
||||
"TelegramIntegrationDesc": "Use telegram integration",
|
||||
"Status": "Status"
|
||||
}
|
||||
}
|
@ -20,6 +20,7 @@
|
||||
"Incoming": "Входящее",
|
||||
"Messages": "Сообщения",
|
||||
"Telegram": "Telegram",
|
||||
"TelegramIntegrationDesc": "Подключить Telegram"
|
||||
"TelegramIntegrationDesc": "Подключить Telegram",
|
||||
"Status": "Статус"
|
||||
}
|
||||
}
|
@ -41,6 +41,8 @@
|
||||
"@anticrm/chunter": "~0.6.0",
|
||||
"@anticrm/login": "~0.6.1",
|
||||
"@anticrm/core": "~0.6.11",
|
||||
"@anticrm/notification-resources": "~0.6.0"
|
||||
"@anticrm/notification-resources": "~0.6.0",
|
||||
"@anticrm/attachment": "~0.6.0",
|
||||
"@anticrm/attachment-resources": "~0.6.0"
|
||||
}
|
||||
}
|
||||
|
@ -14,23 +14,24 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import attachment from '@anticrm/attachment'
|
||||
import { AttachmentRefInput } from '@anticrm/attachment-resources'
|
||||
import contact, { Channel, Contact, EmployeeAccount, formatName } from '@anticrm/contact'
|
||||
import { getCurrentAccount, Ref, SortingOrder, Space } from '@anticrm/core'
|
||||
import login from '@anticrm/login'
|
||||
import { getMetadata } from '@anticrm/platform'
|
||||
import { generateId, getCurrentAccount, Ref, SortingOrder, Space } from '@anticrm/core'
|
||||
import { NotificationClientImpl } from '@anticrm/notification-resources'
|
||||
import { createQuery, getClient } from '@anticrm/presentation'
|
||||
import setting from '@anticrm/setting'
|
||||
import type { SharedTelegramMessage, TelegramMessage } from '@anticrm/telegram'
|
||||
import { ReferenceInput } from '@anticrm/text-editor'
|
||||
import type { NewTelegramMessage, SharedTelegramMessage, TelegramMessage } from '@anticrm/telegram'
|
||||
import { ActionIcon, Button, IconShare, ScrollBox, showPopup } from '@anticrm/ui'
|
||||
import telegram from '../plugin'
|
||||
import Connect from './Connect.svelte'
|
||||
import TelegramIcon from './icons/Telegram.svelte'
|
||||
import Messages from './Messages.svelte'
|
||||
import { NotificationClientImpl } from '@anticrm/notification-resources'
|
||||
|
||||
export let object: Contact
|
||||
let channel: Channel | undefined = undefined
|
||||
let objectId: Ref<NewTelegramMessage> = generateId()
|
||||
|
||||
const client = getClient()
|
||||
const notificationClient = NotificationClientImpl.getClient()
|
||||
|
||||
@ -48,7 +49,6 @@
|
||||
let enabled: boolean
|
||||
let selected: Set<Ref<SharedTelegramMessage>> = new Set<Ref<SharedTelegramMessage>>()
|
||||
let selectable = false
|
||||
const url = getMetadata(login.metadata.TelegramUrl) ?? ''
|
||||
|
||||
const messagesQuery = createQuery()
|
||||
const accauntsQuery = createQuery()
|
||||
@ -67,7 +67,13 @@
|
||||
const accountsIds = new Set(messages.map((p) => p.modifiedBy as Ref<EmployeeAccount>))
|
||||
updateAccountsQuery(accountsIds)
|
||||
},
|
||||
{ sort: { modifiedOn: SortingOrder.Descending }, limit: 500 }
|
||||
{
|
||||
sort: { modifiedOn: SortingOrder.Descending },
|
||||
limit: 500,
|
||||
lookup: {
|
||||
_id: { attachments: attachment.class.Attachment }
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@ -87,63 +93,24 @@
|
||||
}
|
||||
)
|
||||
|
||||
async function sendMsg (to: string, msg: string) {
|
||||
const res = await fetch(url + '/send-msg', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: 'Bearer ' + getMetadata(login.metadata.LoginToken),
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
to,
|
||||
msg
|
||||
})
|
||||
})
|
||||
if (channel !== undefined) {
|
||||
await notificationClient.updateLastView(channel._id, channel._class, undefined, true)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
async function addContact (phone: string) {
|
||||
const [lastName, firstName] = object.name.split(',')
|
||||
|
||||
return await fetch(url + '/add-contact', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: 'Bearer ' + getMetadata(login.metadata.LoginToken),
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
firstName: firstName ?? '',
|
||||
lastName: lastName ?? '',
|
||||
phone
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
async function onMessage (event: CustomEvent) {
|
||||
const to = channel?.value ?? ''
|
||||
const sendRes = await sendMsg(to, event.detail)
|
||||
if (channel === undefined) return
|
||||
const { message, attachments } = event.detail
|
||||
await client.createDoc(
|
||||
telegram.class.NewMessage,
|
||||
telegram.space.Telegram,
|
||||
{
|
||||
content: message,
|
||||
status: 'new',
|
||||
attachments,
|
||||
attachedTo: channel._id,
|
||||
attachedToClass: channel._class,
|
||||
collection: 'newMessages'
|
||||
},
|
||||
objectId
|
||||
)
|
||||
|
||||
if (sendRes.status !== 400 || !to.startsWith('+')) {
|
||||
return
|
||||
}
|
||||
|
||||
const err = await sendRes.json()
|
||||
if (err.code !== 'CONTACT_IMPORT_REQUIRED') {
|
||||
return
|
||||
}
|
||||
|
||||
const addRes = await addContact(to)
|
||||
|
||||
if (Math.trunc(addRes.status / 100) !== 2) {
|
||||
const { message } = await addRes.json().catch(() => ({ message: 'Unknown error' }))
|
||||
|
||||
throw Error(message)
|
||||
}
|
||||
|
||||
await sendMsg(to, event.detail)
|
||||
objectId = generateId()
|
||||
}
|
||||
|
||||
function getName (message: TelegramMessage, accounts: EmployeeAccount[]): string {
|
||||
@ -240,7 +207,12 @@
|
||||
</div>
|
||||
</div>
|
||||
{:else if enabled}
|
||||
<ReferenceInput on:message={onMessage} />
|
||||
<AttachmentRefInput
|
||||
space={telegram.space.Telegram}
|
||||
_class={telegram.class.NewMessage}
|
||||
{objectId}
|
||||
on:message={onMessage}
|
||||
/>
|
||||
{:else}
|
||||
<div class="flex-center">
|
||||
<Button
|
||||
|
@ -14,17 +14,20 @@
|
||||
// limitations under the License.
|
||||
-->
|
||||
<script lang="ts">
|
||||
import type { SharedTelegramMessage } from '@anticrm/telegram'
|
||||
import { MessageViewer } from '@anticrm/presentation'
|
||||
import { Attachment } from '@anticrm/attachment'
|
||||
import { AttachmentList } from '@anticrm/attachment-resources'
|
||||
import { formatName } from '@anticrm/contact'
|
||||
import { CheckBox } from '@anticrm/ui'
|
||||
import { WithLookup } from '@anticrm/core'
|
||||
import { MessageViewer } from '@anticrm/presentation'
|
||||
import type { SharedTelegramMessage } from '@anticrm/telegram'
|
||||
import { CheckBox, getPlatformColorForText } from '@anticrm/ui'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
import { getPlatformColorForText } from '@anticrm/ui'
|
||||
|
||||
export let message: SharedTelegramMessage
|
||||
export let message: WithLookup<SharedTelegramMessage>
|
||||
export let showName: boolean = false
|
||||
export let selected: boolean = false
|
||||
export let selectable: boolean = false
|
||||
$: attachments = message.$lookup?.attachments ? (message.$lookup.attachments as Attachment[]) : undefined
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
</script>
|
||||
@ -32,7 +35,7 @@
|
||||
<div
|
||||
class="flex-between"
|
||||
class:selectable
|
||||
on:click|preventDefault={() => {
|
||||
on:click={() => {
|
||||
dispatch('select', message)
|
||||
}}
|
||||
>
|
||||
@ -41,6 +44,9 @@
|
||||
{#if showName}
|
||||
<div class="name" style="color: {getPlatformColorForText(message.sender)}">{formatName(message.sender)}</div>
|
||||
{/if}
|
||||
{#if attachments}
|
||||
<AttachmentList {attachments} />
|
||||
{/if}
|
||||
<div class="flex">
|
||||
<div class="caption-color mr-4"><MessageViewer message={message.content} /></div>
|
||||
<div class="time">
|
||||
|
@ -22,16 +22,29 @@ import type { IntegrationType, Handler } from '@anticrm/setting'
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface TelegramMessage extends AttachedDoc {
|
||||
export interface BaseTelegramMessage extends Doc {
|
||||
content: string
|
||||
attachments?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface TelegramMessage extends BaseTelegramMessage, AttachedDoc {
|
||||
incoming: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface SharedTelegramMessage extends Doc {
|
||||
content: string
|
||||
export interface NewTelegramMessage extends BaseTelegramMessage, AttachedDoc {
|
||||
status: 'new' | 'sent'
|
||||
}
|
||||
|
||||
/**
|
||||
* @public
|
||||
*/
|
||||
export interface SharedTelegramMessage extends BaseTelegramMessage {
|
||||
incoming: boolean
|
||||
sender: string
|
||||
}
|
||||
@ -62,6 +75,7 @@ export default plugin(telegramId, {
|
||||
},
|
||||
class: {
|
||||
Message: '' as Ref<Class<TelegramMessage>>,
|
||||
NewMessage: '' as Ref<Class<NewTelegramMessage>>,
|
||||
SharedMessage: '' as Ref<Class<SharedTelegramMessage>>,
|
||||
SharedMessages: '' as Ref<Class<SharedTelegramMessages>>
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user