Compare commits

...

2 Commits

Author SHA1 Message Date
Admin 4a5b6dd8aa update upload file 2025-09-19 13:55:03 +07:00
Admin 5c36fa0d3d update upload file 2025-09-19 13:53:22 +07:00
15 changed files with 471 additions and 70 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

@ -35,10 +35,6 @@ class MessageApiService {
const processedArray = await Promise.all(
msg.message.map(async (item) => {
if (isBlobUrl(item)) {
console.log(
"Found blob URL in array, converting to Base64:",
item
);
const base64 = await imageUrlToBase64(item);
return base64;
}

View File

@ -65,6 +65,7 @@ export class TeamsChatService {
// Lấy title, lọc link và trả về {name, url}
const results = children.flatMap((child) => {
const token = this.getAssetToken();
const title = child.getAttribute("title");
if (!title) return [];
@ -77,7 +78,7 @@ export class TeamsChatService {
const urlMatch = lines.find((line) => /^https?:\/\//.test(line));
if (!urlMatch) return [];
return [{ name, url: urlMatch }];
return [{ name, url: urlMatch, token }];
});
return results;
@ -213,6 +214,16 @@ export class TeamsChatService {
const { room_id, room_name } = this.getCurrentRoomInfo();
const isExitsOption = document
.getElementById(`attachments-${dateTime}`)
?.querySelector('[aria-label="More attachment options"]');
console.log({ isExitsOption });
if (isExitsOption) {
(isExitsOption as any)?.click();
}
return {
name: authorEl?.innerText,
message: this._getMessageByEl(contentEl),
@ -223,6 +234,22 @@ export class TeamsChatService {
};
}
getAssetToken() {
const tokenKey = Object.keys(localStorage).find((key) => {
const value = localStorage[key];
return (
value.includes('"credentialType":"AccessToken"') &&
value.includes('"target":"https://graph.microsoft.com/.default')
);
});
const accessToken = tokenKey
? JSON.parse(localStorage[tokenKey]).secret
: null;
return accessToken;
}
extractAllMessages(): IMessage[] {
const myMessages: IMessage[] = Array.from(
document.querySelectorAll(".fui-ChatMyMessage")

View File

@ -2,15 +2,18 @@ import { Conversation } from '@/entities/conversation.entity';
import { Message } from '@/entities/message.entity';
import { formatTextIfValid } from '@/features/conver-message';
import { formatTimeAU } from '@/features/format-time-au';
import { isBase64 } from '@/features/is-base64';
import AppResponse from '@/system/filters/response/app-response';
import { SystemLang } from '@/system/lang/system.lang';
import {
BadRequestException,
HttpStatus,
Injectable,
Logger,
NotFoundException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { isJSON } from 'class-validator';
import { paginate, PaginateQuery } from 'nestjs-paginate';
import { Repository } from 'typeorm';
import { CreateBulkMessageDto } from './dtos/create-bulk-message.dto';
@ -19,10 +22,10 @@ import { ReplyMessageDto } from './dtos/reply-message.dto';
import { SendMessageDto } from './dtos/send-message.dto';
import { MessagesEventService } from './messages-event.service';
import { ZulipService } from './zulip.service';
import { isUrl } from '@/features/is-url';
import { isBase64 } from '@/features/is-base64';
@Injectable()
export class MessagesService {
private readonly logger = new Logger(MessagesService.name);
constructor(
@InjectRepository(Message)
readonly repo: Repository<Message>,
@ -126,6 +129,19 @@ export class MessagesService {
// return { data: result, exit: false };
// }
encodeSharingUrl(url: string) {
let encoded = btoa(url); // Base64 thường
encoded = encoded
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/, '');
return 'u!' + encoded;
}
toDownloadLink(encoded: string) {
return `https://graph.microsoft.com/v1.0/shares/${encoded}/driveItem?select=restricted,webDavUrl,@microsoft.graph.downloadUrl,file,name`;
}
async create(
dto: CreateMessageDto,
): Promise<{ data: Message; exit: boolean }> {
@ -159,14 +175,83 @@ export class MessagesService {
const finalMessages: string[] = [];
// for (const msg of messages) {
// if (isBase64(msg)) {
// // Nếu là base64 → upload lên Zulip trước
// const fileUrl = await this.zulipService.uploadBase64ToZulip(msg);
// finalMessages.push(fileUrl); // Lưu link ảnh thay vì base64
// } else if (isJSON(msg)) {
// console.log({ msg });
// const data = JSON.parse(msg);
// if (!data?.url || !data?.token) {
// finalMessages.push(msg); // Lưu nguyên text
// continue;
// }
// const encoded = this.encodeSharingUrl(data?.url);
// const downloadUrl = this.toDownloadLink(encoded);
// try {
// const fileUrl = await this.zulipService.uploadGraphLinkToZulip({
// url: downloadUrl,
// token: data.token,
// });
// console.log({ fileUrl });
// const newData = { ...data, url: fileUrl, origin_url: data.url };
// finalMessages.push(JSON.stringify(newData)); // Lưu nguyên text
// } catch (error) {
// finalMessages.push(msg); // Lưu nguyên text
// continue;
// }
// finalMessages.push(msg); // Lưu nguyên text
// } else {
// finalMessages.push(msg); // Lưu nguyên text
// }
// }
for (const msg of messages) {
if (isBase64(msg)) {
// Nếu là base64 → upload lên Zulip trước
const fileUrl = await this.zulipService.uploadFileToZulip(msg);
const fileUrl = await this.zulipService.uploadBase64ToZulip(msg);
finalMessages.push(fileUrl); // Lưu link ảnh thay vì base64
} else {
finalMessages.push(msg); // Lưu nguyên text
continue;
}
if (isJSON(msg)) {
const data = JSON.parse(msg);
if (!data?.url || !data?.token) {
finalMessages.push(msg); // Lưu nguyên text nếu thiếu dữ liệu
continue;
}
const encoded = this.encodeSharingUrl(data.url);
const downloadUrl = this.toDownloadLink(encoded);
try {
const fileUrl = await this.zulipService.uploadGraphLinkToZulip({
url: downloadUrl,
token: data.token,
});
const newData = { ...data, url: fileUrl, origin_url: data.url };
finalMessages.push(JSON.stringify(newData)); // ✅ chỉ push data đã cập nhật
} catch (error) {
finalMessages.push(msg); // push message gốc khi lỗi
}
continue; // kết thúc vòng lặp để tránh chạy xuống dưới
}
// Trường hợp còn lại → push nguyên text
finalMessages.push(msg);
}
// 4. Lưu vào DB với message đã xử lý
@ -183,44 +268,12 @@ export class MessagesService {
// 5. Kiểm tra conversation có hợp lệ để gửi Zulip không
const groupPrefix = process.env.GROUP_PREFIX?.toLocaleLowerCase() || '';
if (
!conversation.name.toLocaleLowerCase().includes(groupPrefix) ||
conversation.type !== 'group'
!conversation?.name?.toLocaleLowerCase().includes(groupPrefix) ||
conversation?.type !== 'group'
) {
return { data: result, exit: false };
}
// 6. Build message để gửi lên Zulip
// const buildZulipMessageContent = (
// msgs: string[],
// result: Message,
// ): string => {
// const imageUris: string[] = [];
// const textMessages: string[] = [];
// for (const msg of msgs) {
// // Nếu là link `/user_uploads/...` thì render ảnh
// if (/\/user_uploads\//.test(msg)) {
// imageUris.push(`[image](${msg.replace(/^\/api\/v1/, '')})`);
// } else {
// textMessages.push(formatTextIfValid(msg));
// }
// }
// let finalContent = `** :rocket: ${result.name} sent - ${formatTimeAU(result.time_raw)}:**\n`;
// // Nếu có text → thêm block code
// if (textMessages.length > 0) {
// finalContent += `\`\`\`\n${textMessages.join('\n')}\n\`\`\`\n`;
// }
// // Nếu có ảnh → thêm danh sách ảnh ở cuối
// if (imageUris.length > 0) {
// finalContent += imageUris.join('\n');
// }
// return finalContent.trim();
// };
const buildZulipMessageContent = (
msgs: string[],
result: Message,
@ -230,23 +283,23 @@ export class MessagesService {
const fileLinks: string[] = []; // Chứa danh sách file dạng [name](url)
for (const msg of msgs) {
// 1. Nếu là link ảnh upload → hiển thị dạng image
if (/\/user_uploads\//.test(msg)) {
imageUris.push(`[image](${msg.replace(/^\/api\/v1/, '')})`);
continue;
}
// 2. Nếu là JSON và có type === 'file'
// 1. Nếu là JSON và có type === 'file'
try {
const parsed = JSON.parse(msg);
const parsed = JSON.parse(msg) || msg;
if (parsed?.type === 'file' && parsed.url && parsed.name) {
fileLinks.push(`[${parsed.name}](${parsed.url})`);
continue;
}
} catch {
} catch (error) {
// Không phải JSON → bỏ qua, xử lý như text
}
// 2. Nếu là link ảnh upload → hiển thị dạng image
if (/\/user_uploads\//.test(msg)) {
imageUris.push(`[image](${msg.replace(/^\/api\/v1/, '')})`);
continue;
}
// 3. Còn lại là text thường
textMessages.push(formatTextIfValid(msg));
}
@ -389,19 +442,26 @@ export class MessagesService {
}
async createAndSendToZulip({ data }: CreateBulkMessageDto) {
const results = [];
// Lọc message hợp lệ
const filteredData = data.filter((message) => message?.message?.length);
// Đảo ngược array trước khi xử lý
// const reversedData = [...data].reverse();
for (const mes of data.filter((message) => message.message.length)) {
const result = await this.create(mes);
if (result) {
results.push(result);
// Map thành danh sách Promise
const promises = filteredData.map(async (mes) => {
try {
const result = await this.create(mes);
return result ?? null; // nếu result là undefined/null
} catch (error) {
this.logger.error(`Failed to create message`, { mes, error });
return null; // Đánh dấu lỗi, không throw
}
}
});
return results;
// Chạy tất cả promise cùng lúc
const results = await Promise.allSettled(promises);
// Lấy ra các kết quả thành công và không null
return results
.filter((res) => res.status === 'fulfilled' && res.value)
.map((res) => (res as PromiseFulfilledResult<any>).value);
}
}

View File

@ -27,7 +27,7 @@ export class ZulipService {
/**
* Upload bất kỳ file nào từ external URL lên Zulip
*/
async uploadFileToZulip(base64Data: string): Promise<string> {
async uploadBase64ToZulip(base64Data: string): Promise<string> {
const tempDir = path.join(process.cwd(), 'tmp');
await fs.ensureDir(tempDir);
@ -91,6 +91,90 @@ export class ZulipService {
}
}
/**
* Upload file từ Microsoft Graph share link lên Zulip
*/
async uploadGraphLinkToZulip({
url,
token,
}: {
url: string;
token: string;
}): Promise<string> {
const tempDir = path.join(process.cwd(), 'tmp');
await fs.ensureDir(tempDir);
let filePath = '';
try {
this.logger.log(`Fetching file metadata from Graph API: ${url}`);
console.log({ token });
// 1. Gọi Graph API để lấy thông tin file
const graphRes = await axios.get(url, {
headers: {
Authorization: `Bearer ${token.trim()}`, // token lấy từ MS Graph
},
});
const data = graphRes.data;
const downloadUrl = data['@microsoft.graph.downloadUrl'];
const fileName = data.name || 'downloaded-file';
if (!downloadUrl) {
throw new Error('Cannot find download URL in Graph response.');
}
console.log({ downloadUrl });
this.logger.log(`Direct download URL: ${downloadUrl}`);
// 2. Tải file từ downloadUrl
const response = await axios.get(downloadUrl, {
responseType: 'arraybuffer',
});
const extension = path.extname(fileName) || '.bin';
const tempFileName = `${Date.now()}-${Math.random().toString(36).substring(7)}${extension}`;
filePath = path.join(tempDir, tempFileName);
await fs.writeFile(filePath, response.data);
this.logger.log(`File saved temporarily at: ${filePath}`);
// 3. Upload file lên Zulip
const form = new FormData();
form.append('filename', fs.createReadStream(filePath));
const uploadRes = await axios.post(
`${this.zulipUrl}/user_uploads`,
form,
{
auth: {
username: this.botEmail,
password: this.apiKey,
},
headers: form.getHeaders(),
},
);
const zulipFileUrl = uploadRes.data.uri;
this.logger.log(`Uploaded to Zulip: ${zulipFileUrl}`);
return zulipFileUrl;
} catch (error: any) {
this.logger.error(
'Upload to Zulip failed',
error?.response?.data || error.message,
);
throw error;
} finally {
// 4. Xóa file tạm
if (filePath && (await fs.pathExists(filePath))) {
// await fs.remove(filePath);
this.logger.log(`Deleted temp file: ${filePath}`);
}
}
}
/**
* Gửi tin nhắn vào một topic trong stream
*/

View File

@ -0,0 +1,26 @@
<a
role="menuitem"
tabindex="-1"
aria-disabled="false"
data-is-focusable="true"
class="ui-menu__item li cs p lj gh mt cv cw cx lm ln mu mv mw"
><span class="ui-menu__itemicon g i h id ie ng nh ci do ni nj"
><span role="img" aria-hidden="true" class="ui-icon ia o ib"
><svg
role="presentation"
focusable="false"
viewBox="2 2 16 16"
class="cs id ie if ig"
>
<path
class="ui-icon__outline cs"
d="M15.5 16.9989C15.7761 16.9989 16 17.2227 16 17.4989C16 17.7443 15.8231 17.9485 15.5899 17.9908L15.5 17.9989H4.5C4.22386 17.9989 4 17.775 4 17.4989C4 17.2534 4.17688 17.0493 4.41012 17.0069L4.5 16.9989H15.5ZM10.0001 2.0011C10.2456 2.0011 10.4497 2.1781 10.492 2.41137L10.5 2.50124L10.496 14.2951L14.1414 10.6468C14.3148 10.473 14.5842 10.4535 14.7792 10.5883L14.8485 10.6461C15.0222 10.8195 15.0418 11.0889 14.907 11.2839L14.8492 11.3532L10.3574 15.8532C10.285 15.9259 10.1957 15.9715 10.1021 15.9902L9.99608 16C9.83511 16 9.69192 15.9239 9.60051 15.8057L5.14386 11.3538C4.94846 11.1587 4.94823 10.8421 5.14336 10.6467C5.3168 10.473 5.58621 10.4535 5.78117 10.5884L5.85046 10.6462L9.496 14.2871L9.5 2.50095C9.50008 2.22481 9.724 2.0011 10.0001 2.0011Z"
></path>
<path
class="ui-icon__filled co"
d="M15.2444 16.4976C15.6586 16.4976 15.9944 16.8334 15.9944 17.2476C15.9944 17.6273 15.7123 17.9411 15.3462 17.9907L15.2444 17.9976H4.74976C4.33554 17.9976 3.99976 17.6618 3.99976 17.2476C3.99976 16.8679 4.28191 16.5541 4.64799 16.5044L4.74976 16.4976H15.2444ZM10.0034 1.99939C10.3831 1.99939 10.6969 2.28154 10.7465 2.64762L10.7534 2.74939L10.7528 12.9424L13.719 9.97181C13.985 9.7053 14.4016 9.68071 14.6955 9.89829L14.7796 9.97083C15.0461 10.2369 15.0707 10.6535 14.8532 10.9473L14.7806 11.0315L10.5378 15.2821L10.4671 15.3446L10.3762 15.4032L10.3397 15.4244L10.2486 15.4623L10.1281 15.4924L10.0588 15.5003L9.99876 15.5023C9.94864 15.5023 9.89776 15.4968 9.84804 15.4862L9.76716 15.4631C9.67115 15.4321 9.58404 15.3818 9.50989 15.3171L5.22032 11.0324C4.92721 10.7397 4.92688 10.2648 5.21956 9.9717C5.48564 9.70524 5.90228 9.68074 6.19605 9.89838L6.28022 9.97094L9.25276 12.9384L9.25338 2.74939C9.25338 2.33518 9.58917 1.99939 10.0034 1.99939Z"
></path></svg></span></span
><span class="ui-menu__itemcontent mi mj mk ml ib r nk nl nm nn" dir="auto"
>Download</span
></a
>

View File

@ -0,0 +1,26 @@
<a
role="menuitem"
tabindex="-1"
aria-disabled="false"
data-is-focusable="true"
class="ui-menu__item li cs p lj gh mt cv cw cx lm ln mu mv mw"
><span class="ui-menu__itemicon g i h id ie ng nh ci do ni nj"
><span role="img" aria-hidden="true" class="ui-icon ia o ib"
><svg
role="presentation"
focusable="false"
viewBox="2 2 16 16"
class="cs id ie if ig"
>
<path
class="ui-icon__outline cs"
d="M15.5 16.9989C15.7761 16.9989 16 17.2227 16 17.4989C16 17.7443 15.8231 17.9485 15.5899 17.9908L15.5 17.9989H4.5C4.22386 17.9989 4 17.775 4 17.4989C4 17.2534 4.17688 17.0493 4.41012 17.0069L4.5 16.9989H15.5ZM10.0001 2.0011C10.2456 2.0011 10.4497 2.1781 10.492 2.41137L10.5 2.50124L10.496 14.2951L14.1414 10.6468C14.3148 10.473 14.5842 10.4535 14.7792 10.5883L14.8485 10.6461C15.0222 10.8195 15.0418 11.0889 14.907 11.2839L14.8492 11.3532L10.3574 15.8532C10.285 15.9259 10.1957 15.9715 10.1021 15.9902L9.99608 16C9.83511 16 9.69192 15.9239 9.60051 15.8057L5.14386 11.3538C4.94846 11.1587 4.94823 10.8421 5.14336 10.6467C5.3168 10.473 5.58621 10.4535 5.78117 10.5884L5.85046 10.6462L9.496 14.2871L9.5 2.50095C9.50008 2.22481 9.724 2.0011 10.0001 2.0011Z"
></path>
<path
class="ui-icon__filled co"
d="M15.2444 16.4976C15.6586 16.4976 15.9944 16.8334 15.9944 17.2476C15.9944 17.6273 15.7123 17.9411 15.3462 17.9907L15.2444 17.9976H4.74976C4.33554 17.9976 3.99976 17.6618 3.99976 17.2476C3.99976 16.8679 4.28191 16.5541 4.64799 16.5044L4.74976 16.4976H15.2444ZM10.0034 1.99939C10.3831 1.99939 10.6969 2.28154 10.7465 2.64762L10.7534 2.74939L10.7528 12.9424L13.719 9.97181C13.985 9.7053 14.4016 9.68071 14.6955 9.89829L14.7796 9.97083C15.0461 10.2369 15.0707 10.6535 14.8532 10.9473L14.7806 11.0315L10.5378 15.2821L10.4671 15.3446L10.3762 15.4032L10.3397 15.4244L10.2486 15.4623L10.1281 15.4924L10.0588 15.5003L9.99876 15.5023C9.94864 15.5023 9.89776 15.4968 9.84804 15.4862L9.76716 15.4631C9.67115 15.4321 9.58404 15.3818 9.50989 15.3171L5.22032 11.0324C4.92721 10.7397 4.92688 10.2648 5.21956 9.9717C5.48564 9.70524 5.90228 9.68074 6.19605 9.89838L6.28022 9.97094L9.25276 12.9384L9.25338 2.74939C9.25338 2.33518 9.58917 1.99939 10.0034 1.99939Z"
></path></svg></span></span
><span class="ui-menu__itemcontent mi mj mk ml ib r nk nl nm nn" dir="auto"
>Download</span
></a
>

View File

@ -0,0 +1,26 @@
<a
role="menuitem"
tabindex="-1"
aria-disabled="false"
data-is-focusable="true"
class="ui-menu__item li cs p lj gh mt cv cw cx lm ln mu mv mw"
><span class="ui-menu__itemicon g i h id ie ng nh ci do ni nj"
><span role="img" aria-hidden="true" class="ui-icon ia o ib"
><svg
role="presentation"
focusable="false"
viewBox="2 2 16 16"
class="cs id ie if ig"
>
<path
class="ui-icon__outline cs"
d="M15.5 16.9989C15.7761 16.9989 16 17.2227 16 17.4989C16 17.7443 15.8231 17.9485 15.5899 17.9908L15.5 17.9989H4.5C4.22386 17.9989 4 17.775 4 17.4989C4 17.2534 4.17688 17.0493 4.41012 17.0069L4.5 16.9989H15.5ZM10.0001 2.0011C10.2456 2.0011 10.4497 2.1781 10.492 2.41137L10.5 2.50124L10.496 14.2951L14.1414 10.6468C14.3148 10.473 14.5842 10.4535 14.7792 10.5883L14.8485 10.6461C15.0222 10.8195 15.0418 11.0889 14.907 11.2839L14.8492 11.3532L10.3574 15.8532C10.285 15.9259 10.1957 15.9715 10.1021 15.9902L9.99608 16C9.83511 16 9.69192 15.9239 9.60051 15.8057L5.14386 11.3538C4.94846 11.1587 4.94823 10.8421 5.14336 10.6467C5.3168 10.473 5.58621 10.4535 5.78117 10.5884L5.85046 10.6462L9.496 14.2871L9.5 2.50095C9.50008 2.22481 9.724 2.0011 10.0001 2.0011Z"
></path>
<path
class="ui-icon__filled co"
d="M15.2444 16.4976C15.6586 16.4976 15.9944 16.8334 15.9944 17.2476C15.9944 17.6273 15.7123 17.9411 15.3462 17.9907L15.2444 17.9976H4.74976C4.33554 17.9976 3.99976 17.6618 3.99976 17.2476C3.99976 16.8679 4.28191 16.5541 4.64799 16.5044L4.74976 16.4976H15.2444ZM10.0034 1.99939C10.3831 1.99939 10.6969 2.28154 10.7465 2.64762L10.7534 2.74939L10.7528 12.9424L13.719 9.97181C13.985 9.7053 14.4016 9.68071 14.6955 9.89829L14.7796 9.97083C15.0461 10.2369 15.0707 10.6535 14.8532 10.9473L14.7806 11.0315L10.5378 15.2821L10.4671 15.3446L10.3762 15.4032L10.3397 15.4244L10.2486 15.4623L10.1281 15.4924L10.0588 15.5003L9.99876 15.5023C9.94864 15.5023 9.89776 15.4968 9.84804 15.4862L9.76716 15.4631C9.67115 15.4321 9.58404 15.3818 9.50989 15.3171L5.22032 11.0324C4.92721 10.7397 4.92688 10.2648 5.21956 9.9717C5.48564 9.70524 5.90228 9.68074 6.19605 9.89838L6.28022 9.97094L9.25276 12.9384L9.25338 2.74939C9.25338 2.33518 9.58917 1.99939 10.0034 1.99939Z"
></path></svg></span></span
><span class="ui-menu__itemcontent mi mj mk ml ib r nk nl nm nn" dir="auto"
>Download</span
></a
>

View File

@ -0,0 +1,26 @@
<a
role="menuitem"
tabindex="-1"
aria-disabled="false"
data-is-focusable="true"
class="ui-menu__item li cs p lj gh mt cv cw cx lm ln mu mv mw"
><span class="ui-menu__itemicon g i h id ie ng nh ci do ni nj"
><span role="img" aria-hidden="true" class="ui-icon ia o ib"
><svg
role="presentation"
focusable="false"
viewBox="2 2 16 16"
class="cs id ie if ig"
>
<path
class="ui-icon__outline cs"
d="M15.5 16.9989C15.7761 16.9989 16 17.2227 16 17.4989C16 17.7443 15.8231 17.9485 15.5899 17.9908L15.5 17.9989H4.5C4.22386 17.9989 4 17.775 4 17.4989C4 17.2534 4.17688 17.0493 4.41012 17.0069L4.5 16.9989H15.5ZM10.0001 2.0011C10.2456 2.0011 10.4497 2.1781 10.492 2.41137L10.5 2.50124L10.496 14.2951L14.1414 10.6468C14.3148 10.473 14.5842 10.4535 14.7792 10.5883L14.8485 10.6461C15.0222 10.8195 15.0418 11.0889 14.907 11.2839L14.8492 11.3532L10.3574 15.8532C10.285 15.9259 10.1957 15.9715 10.1021 15.9902L9.99608 16C9.83511 16 9.69192 15.9239 9.60051 15.8057L5.14386 11.3538C4.94846 11.1587 4.94823 10.8421 5.14336 10.6467C5.3168 10.473 5.58621 10.4535 5.78117 10.5884L5.85046 10.6462L9.496 14.2871L9.5 2.50095C9.50008 2.22481 9.724 2.0011 10.0001 2.0011Z"
></path>
<path
class="ui-icon__filled co"
d="M15.2444 16.4976C15.6586 16.4976 15.9944 16.8334 15.9944 17.2476C15.9944 17.6273 15.7123 17.9411 15.3462 17.9907L15.2444 17.9976H4.74976C4.33554 17.9976 3.99976 17.6618 3.99976 17.2476C3.99976 16.8679 4.28191 16.5541 4.64799 16.5044L4.74976 16.4976H15.2444ZM10.0034 1.99939C10.3831 1.99939 10.6969 2.28154 10.7465 2.64762L10.7534 2.74939L10.7528 12.9424L13.719 9.97181C13.985 9.7053 14.4016 9.68071 14.6955 9.89829L14.7796 9.97083C15.0461 10.2369 15.0707 10.6535 14.8532 10.9473L14.7806 11.0315L10.5378 15.2821L10.4671 15.3446L10.3762 15.4032L10.3397 15.4244L10.2486 15.4623L10.1281 15.4924L10.0588 15.5003L9.99876 15.5023C9.94864 15.5023 9.89776 15.4968 9.84804 15.4862L9.76716 15.4631C9.67115 15.4321 9.58404 15.3818 9.50989 15.3171L5.22032 11.0324C4.92721 10.7397 4.92688 10.2648 5.21956 9.9717C5.48564 9.70524 5.90228 9.68074 6.19605 9.89838L6.28022 9.97094L9.25276 12.9384L9.25338 2.74939C9.25338 2.33518 9.58917 1.99939 10.0034 1.99939Z"
></path></svg></span></span
><span class="ui-menu__itemcontent mi mj mk ml ib r nk nl nm nn" dir="auto"
>Download</span
></a
>

View File

@ -0,0 +1,26 @@
<a
role="menuitem"
tabindex="-1"
aria-disabled="false"
data-is-focusable="true"
class="ui-menu__item li cs p lj gh mt cv cw cx lm ln mu mv mw"
><span class="ui-menu__itemicon g i h id ie ng nh ci do ni nj"
><span role="img" aria-hidden="true" class="ui-icon ia o ib"
><svg
role="presentation"
focusable="false"
viewBox="2 2 16 16"
class="cs id ie if ig"
>
<path
class="ui-icon__outline cs"
d="M15.5 16.9989C15.7761 16.9989 16 17.2227 16 17.4989C16 17.7443 15.8231 17.9485 15.5899 17.9908L15.5 17.9989H4.5C4.22386 17.9989 4 17.775 4 17.4989C4 17.2534 4.17688 17.0493 4.41012 17.0069L4.5 16.9989H15.5ZM10.0001 2.0011C10.2456 2.0011 10.4497 2.1781 10.492 2.41137L10.5 2.50124L10.496 14.2951L14.1414 10.6468C14.3148 10.473 14.5842 10.4535 14.7792 10.5883L14.8485 10.6461C15.0222 10.8195 15.0418 11.0889 14.907 11.2839L14.8492 11.3532L10.3574 15.8532C10.285 15.9259 10.1957 15.9715 10.1021 15.9902L9.99608 16C9.83511 16 9.69192 15.9239 9.60051 15.8057L5.14386 11.3538C4.94846 11.1587 4.94823 10.8421 5.14336 10.6467C5.3168 10.473 5.58621 10.4535 5.78117 10.5884L5.85046 10.6462L9.496 14.2871L9.5 2.50095C9.50008 2.22481 9.724 2.0011 10.0001 2.0011Z"
></path>
<path
class="ui-icon__filled co"
d="M15.2444 16.4976C15.6586 16.4976 15.9944 16.8334 15.9944 17.2476C15.9944 17.6273 15.7123 17.9411 15.3462 17.9907L15.2444 17.9976H4.74976C4.33554 17.9976 3.99976 17.6618 3.99976 17.2476C3.99976 16.8679 4.28191 16.5541 4.64799 16.5044L4.74976 16.4976H15.2444ZM10.0034 1.99939C10.3831 1.99939 10.6969 2.28154 10.7465 2.64762L10.7534 2.74939L10.7528 12.9424L13.719 9.97181C13.985 9.7053 14.4016 9.68071 14.6955 9.89829L14.7796 9.97083C15.0461 10.2369 15.0707 10.6535 14.8532 10.9473L14.7806 11.0315L10.5378 15.2821L10.4671 15.3446L10.3762 15.4032L10.3397 15.4244L10.2486 15.4623L10.1281 15.4924L10.0588 15.5003L9.99876 15.5023C9.94864 15.5023 9.89776 15.4968 9.84804 15.4862L9.76716 15.4631C9.67115 15.4321 9.58404 15.3818 9.50989 15.3171L5.22032 11.0324C4.92721 10.7397 4.92688 10.2648 5.21956 9.9717C5.48564 9.70524 5.90228 9.68074 6.19605 9.89838L6.28022 9.97094L9.25276 12.9384L9.25338 2.74939C9.25338 2.33518 9.58917 1.99939 10.0034 1.99939Z"
></path></svg></span></span
><span class="ui-menu__itemcontent mi mj mk ml ib r nk nl nm nn" dir="auto"
>Download</span
></a
>

View File

@ -0,0 +1,26 @@
<a
role="menuitem"
tabindex="-1"
aria-disabled="false"
data-is-focusable="true"
class="ui-menu__item li cs p lj gh mt cv cw cx lm ln mu mv mw"
><span class="ui-menu__itemicon g i h id ie ng nh ci do ni nj"
><span role="img" aria-hidden="true" class="ui-icon ia o ib"
><svg
role="presentation"
focusable="false"
viewBox="2 2 16 16"
class="cs id ie if ig"
>
<path
class="ui-icon__outline cs"
d="M15.5 16.9989C15.7761 16.9989 16 17.2227 16 17.4989C16 17.7443 15.8231 17.9485 15.5899 17.9908L15.5 17.9989H4.5C4.22386 17.9989 4 17.775 4 17.4989C4 17.2534 4.17688 17.0493 4.41012 17.0069L4.5 16.9989H15.5ZM10.0001 2.0011C10.2456 2.0011 10.4497 2.1781 10.492 2.41137L10.5 2.50124L10.496 14.2951L14.1414 10.6468C14.3148 10.473 14.5842 10.4535 14.7792 10.5883L14.8485 10.6461C15.0222 10.8195 15.0418 11.0889 14.907 11.2839L14.8492 11.3532L10.3574 15.8532C10.285 15.9259 10.1957 15.9715 10.1021 15.9902L9.99608 16C9.83511 16 9.69192 15.9239 9.60051 15.8057L5.14386 11.3538C4.94846 11.1587 4.94823 10.8421 5.14336 10.6467C5.3168 10.473 5.58621 10.4535 5.78117 10.5884L5.85046 10.6462L9.496 14.2871L9.5 2.50095C9.50008 2.22481 9.724 2.0011 10.0001 2.0011Z"
></path>
<path
class="ui-icon__filled co"
d="M15.2444 16.4976C15.6586 16.4976 15.9944 16.8334 15.9944 17.2476C15.9944 17.6273 15.7123 17.9411 15.3462 17.9907L15.2444 17.9976H4.74976C4.33554 17.9976 3.99976 17.6618 3.99976 17.2476C3.99976 16.8679 4.28191 16.5541 4.64799 16.5044L4.74976 16.4976H15.2444ZM10.0034 1.99939C10.3831 1.99939 10.6969 2.28154 10.7465 2.64762L10.7534 2.74939L10.7528 12.9424L13.719 9.97181C13.985 9.7053 14.4016 9.68071 14.6955 9.89829L14.7796 9.97083C15.0461 10.2369 15.0707 10.6535 14.8532 10.9473L14.7806 11.0315L10.5378 15.2821L10.4671 15.3446L10.3762 15.4032L10.3397 15.4244L10.2486 15.4623L10.1281 15.4924L10.0588 15.5003L9.99876 15.5023C9.94864 15.5023 9.89776 15.4968 9.84804 15.4862L9.76716 15.4631C9.67115 15.4321 9.58404 15.3818 9.50989 15.3171L5.22032 11.0324C4.92721 10.7397 4.92688 10.2648 5.21956 9.9717C5.48564 9.70524 5.90228 9.68074 6.19605 9.89838L6.28022 9.97094L9.25276 12.9384L9.25338 2.74939C9.25338 2.33518 9.58917 1.99939 10.0034 1.99939Z"
></path></svg></span></span
><span class="ui-menu__itemcontent mi mj mk ml ib r nk nl nm nn" dir="auto"
>Download</span
></a
>

View File

@ -0,0 +1,26 @@
<a
role="menuitem"
tabindex="-1"
aria-disabled="false"
data-is-focusable="true"
class="ui-menu__item li cs p lj gh mt cv cw cx lm ln mu mv mw"
><span class="ui-menu__itemicon g i h id ie ng nh ci do ni nj"
><span role="img" aria-hidden="true" class="ui-icon ia o ib"
><svg
role="presentation"
focusable="false"
viewBox="2 2 16 16"
class="cs id ie if ig"
>
<path
class="ui-icon__outline cs"
d="M15.5 16.9989C15.7761 16.9989 16 17.2227 16 17.4989C16 17.7443 15.8231 17.9485 15.5899 17.9908L15.5 17.9989H4.5C4.22386 17.9989 4 17.775 4 17.4989C4 17.2534 4.17688 17.0493 4.41012 17.0069L4.5 16.9989H15.5ZM10.0001 2.0011C10.2456 2.0011 10.4497 2.1781 10.492 2.41137L10.5 2.50124L10.496 14.2951L14.1414 10.6468C14.3148 10.473 14.5842 10.4535 14.7792 10.5883L14.8485 10.6461C15.0222 10.8195 15.0418 11.0889 14.907 11.2839L14.8492 11.3532L10.3574 15.8532C10.285 15.9259 10.1957 15.9715 10.1021 15.9902L9.99608 16C9.83511 16 9.69192 15.9239 9.60051 15.8057L5.14386 11.3538C4.94846 11.1587 4.94823 10.8421 5.14336 10.6467C5.3168 10.473 5.58621 10.4535 5.78117 10.5884L5.85046 10.6462L9.496 14.2871L9.5 2.50095C9.50008 2.22481 9.724 2.0011 10.0001 2.0011Z"
></path>
<path
class="ui-icon__filled co"
d="M15.2444 16.4976C15.6586 16.4976 15.9944 16.8334 15.9944 17.2476C15.9944 17.6273 15.7123 17.9411 15.3462 17.9907L15.2444 17.9976H4.74976C4.33554 17.9976 3.99976 17.6618 3.99976 17.2476C3.99976 16.8679 4.28191 16.5541 4.64799 16.5044L4.74976 16.4976H15.2444ZM10.0034 1.99939C10.3831 1.99939 10.6969 2.28154 10.7465 2.64762L10.7534 2.74939L10.7528 12.9424L13.719 9.97181C13.985 9.7053 14.4016 9.68071 14.6955 9.89829L14.7796 9.97083C15.0461 10.2369 15.0707 10.6535 14.8532 10.9473L14.7806 11.0315L10.5378 15.2821L10.4671 15.3446L10.3762 15.4032L10.3397 15.4244L10.2486 15.4623L10.1281 15.4924L10.0588 15.5003L9.99876 15.5023C9.94864 15.5023 9.89776 15.4968 9.84804 15.4862L9.76716 15.4631C9.67115 15.4321 9.58404 15.3818 9.50989 15.3171L5.22032 11.0324C4.92721 10.7397 4.92688 10.2648 5.21956 9.9717C5.48564 9.70524 5.90228 9.68074 6.19605 9.89838L6.28022 9.97094L9.25276 12.9384L9.25338 2.74939C9.25338 2.33518 9.58917 1.99939 10.0034 1.99939Z"
></path></svg></span></span
><span class="ui-menu__itemcontent mi mj mk ml ib r nk nl nm nn" dir="auto"
>Download</span
></a
>

View File

@ -0,0 +1,26 @@
<a
role="menuitem"
tabindex="-1"
aria-disabled="false"
data-is-focusable="true"
class="ui-menu__item li cs p lj gh mt cv cw cx lm ln mu mv mw"
><span class="ui-menu__itemicon g i h id ie ng nh ci do ni nj"
><span role="img" aria-hidden="true" class="ui-icon ia o ib"
><svg
role="presentation"
focusable="false"
viewBox="2 2 16 16"
class="cs id ie if ig"
>
<path
class="ui-icon__outline cs"
d="M15.5 16.9989C15.7761 16.9989 16 17.2227 16 17.4989C16 17.7443 15.8231 17.9485 15.5899 17.9908L15.5 17.9989H4.5C4.22386 17.9989 4 17.775 4 17.4989C4 17.2534 4.17688 17.0493 4.41012 17.0069L4.5 16.9989H15.5ZM10.0001 2.0011C10.2456 2.0011 10.4497 2.1781 10.492 2.41137L10.5 2.50124L10.496 14.2951L14.1414 10.6468C14.3148 10.473 14.5842 10.4535 14.7792 10.5883L14.8485 10.6461C15.0222 10.8195 15.0418 11.0889 14.907 11.2839L14.8492 11.3532L10.3574 15.8532C10.285 15.9259 10.1957 15.9715 10.1021 15.9902L9.99608 16C9.83511 16 9.69192 15.9239 9.60051 15.8057L5.14386 11.3538C4.94846 11.1587 4.94823 10.8421 5.14336 10.6467C5.3168 10.473 5.58621 10.4535 5.78117 10.5884L5.85046 10.6462L9.496 14.2871L9.5 2.50095C9.50008 2.22481 9.724 2.0011 10.0001 2.0011Z"
></path>
<path
class="ui-icon__filled co"
d="M15.2444 16.4976C15.6586 16.4976 15.9944 16.8334 15.9944 17.2476C15.9944 17.6273 15.7123 17.9411 15.3462 17.9907L15.2444 17.9976H4.74976C4.33554 17.9976 3.99976 17.6618 3.99976 17.2476C3.99976 16.8679 4.28191 16.5541 4.64799 16.5044L4.74976 16.4976H15.2444ZM10.0034 1.99939C10.3831 1.99939 10.6969 2.28154 10.7465 2.64762L10.7534 2.74939L10.7528 12.9424L13.719 9.97181C13.985 9.7053 14.4016 9.68071 14.6955 9.89829L14.7796 9.97083C15.0461 10.2369 15.0707 10.6535 14.8532 10.9473L14.7806 11.0315L10.5378 15.2821L10.4671 15.3446L10.3762 15.4032L10.3397 15.4244L10.2486 15.4623L10.1281 15.4924L10.0588 15.5003L9.99876 15.5023C9.94864 15.5023 9.89776 15.4968 9.84804 15.4862L9.76716 15.4631C9.67115 15.4321 9.58404 15.3818 9.50989 15.3171L5.22032 11.0324C4.92721 10.7397 4.92688 10.2648 5.21956 9.9717C5.48564 9.70524 5.90228 9.68074 6.19605 9.89838L6.28022 9.97094L9.25276 12.9384L9.25338 2.74939C9.25338 2.33518 9.58917 1.99939 10.0034 1.99939Z"
></path></svg></span></span
><span class="ui-menu__itemcontent mi mj mk ml ib r nk nl nm nn" dir="auto"
>Download</span
></a
>

View File

@ -0,0 +1,26 @@
<a
role="menuitem"
tabindex="-1"
aria-disabled="false"
data-is-focusable="true"
class="ui-menu__item li cs p lj gh mt cv cw cx lm ln mu mv mw"
><span class="ui-menu__itemicon g i h id ie ng nh ci do ni nj"
><span role="img" aria-hidden="true" class="ui-icon ia o ib"
><svg
role="presentation"
focusable="false"
viewBox="2 2 16 16"
class="cs id ie if ig"
>
<path
class="ui-icon__outline cs"
d="M15.5 16.9989C15.7761 16.9989 16 17.2227 16 17.4989C16 17.7443 15.8231 17.9485 15.5899 17.9908L15.5 17.9989H4.5C4.22386 17.9989 4 17.775 4 17.4989C4 17.2534 4.17688 17.0493 4.41012 17.0069L4.5 16.9989H15.5ZM10.0001 2.0011C10.2456 2.0011 10.4497 2.1781 10.492 2.41137L10.5 2.50124L10.496 14.2951L14.1414 10.6468C14.3148 10.473 14.5842 10.4535 14.7792 10.5883L14.8485 10.6461C15.0222 10.8195 15.0418 11.0889 14.907 11.2839L14.8492 11.3532L10.3574 15.8532C10.285 15.9259 10.1957 15.9715 10.1021 15.9902L9.99608 16C9.83511 16 9.69192 15.9239 9.60051 15.8057L5.14386 11.3538C4.94846 11.1587 4.94823 10.8421 5.14336 10.6467C5.3168 10.473 5.58621 10.4535 5.78117 10.5884L5.85046 10.6462L9.496 14.2871L9.5 2.50095C9.50008 2.22481 9.724 2.0011 10.0001 2.0011Z"
></path>
<path
class="ui-icon__filled co"
d="M15.2444 16.4976C15.6586 16.4976 15.9944 16.8334 15.9944 17.2476C15.9944 17.6273 15.7123 17.9411 15.3462 17.9907L15.2444 17.9976H4.74976C4.33554 17.9976 3.99976 17.6618 3.99976 17.2476C3.99976 16.8679 4.28191 16.5541 4.64799 16.5044L4.74976 16.4976H15.2444ZM10.0034 1.99939C10.3831 1.99939 10.6969 2.28154 10.7465 2.64762L10.7534 2.74939L10.7528 12.9424L13.719 9.97181C13.985 9.7053 14.4016 9.68071 14.6955 9.89829L14.7796 9.97083C15.0461 10.2369 15.0707 10.6535 14.8532 10.9473L14.7806 11.0315L10.5378 15.2821L10.4671 15.3446L10.3762 15.4032L10.3397 15.4244L10.2486 15.4623L10.1281 15.4924L10.0588 15.5003L9.99876 15.5023C9.94864 15.5023 9.89776 15.4968 9.84804 15.4862L9.76716 15.4631C9.67115 15.4321 9.58404 15.3818 9.50989 15.3171L5.22032 11.0324C4.92721 10.7397 4.92688 10.2648 5.21956 9.9717C5.48564 9.70524 5.90228 9.68074 6.19605 9.89838L6.28022 9.97094L9.25276 12.9384L9.25338 2.74939C9.25338 2.33518 9.58917 1.99939 10.0034 1.99939Z"
></path></svg></span></span
><span class="ui-menu__itemcontent mi mj mk ml ib r nk nl nm nn" dir="auto"
>Download</span
></a
>