update upload file
This commit is contained in:
parent
71756bbf4b
commit
5c36fa0d3d
File diff suppressed because one or more lines are too long
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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
|
||||
>
|
||||
|
|
@ -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
|
||||
>
|
||||
|
|
@ -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
|
||||
>
|
||||
|
|
@ -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
|
||||
>
|
||||
|
|
@ -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
|
||||
>
|
||||
|
|
@ -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
|
||||
>
|
||||
|
|
@ -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
|
||||
>
|
||||
|
|
@ -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
|
||||
>
|
||||
|
|
@ -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
|
||||
>
|
||||
Binary file not shown.
|
|
@ -0,0 +1 @@
|
|||
python -m uvicorn main:app --reload --host 127.0.0.1 --port 8443 --ssl-keyfile=key.pem --ssl-certfile=cert.pem
|
||||
Binary file not shown.
|
|
@ -0,0 +1,101 @@
|
|||
from fastapi import FastAPI, Body
|
||||
from pydantic import BaseModel
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
from openai import OpenAI
|
||||
import pyautogui
|
||||
import os
|
||||
import time
|
||||
import pyperclip
|
||||
|
||||
print("Di chuot den o nhap tin nhan trong 5 giay ...")
|
||||
time.sleep(5)
|
||||
input_position = pyautogui.position()
|
||||
|
||||
print(f"Da lay toa do: {input_position}")
|
||||
|
||||
x,y = input_position
|
||||
|
||||
# Thiết lập API key cho OpenAI
|
||||
client = OpenAI(api_key="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") # Hoặc gán trực tiếp: "sk-..."
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
# Cấu hình CORS
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=["*"],
|
||||
allow_credentials=True,
|
||||
allow_methods=["*"],
|
||||
allow_headers=["*"],
|
||||
)
|
||||
|
||||
# Dữ liệu đầu vào cho API
|
||||
class Message(BaseModel):
|
||||
name: str
|
||||
message: str
|
||||
time: int
|
||||
room_id: str
|
||||
room_name: str
|
||||
x: int
|
||||
y: int
|
||||
|
||||
# Gọi OpenAI và giả lập nhập liệu
|
||||
def generate_and_type_reply(sender: str, content: str, x: int, y: int):
|
||||
try:
|
||||
response = client.chat.completions.create(
|
||||
model="gpt-4o-mini",
|
||||
messages=[
|
||||
{"role": "system", "content": "Ban la tro ly tin nhan AI tra loi ngan gon va day du y, than thien. Tra loi tieng Anh. Format xuong dong bang \n"},
|
||||
{"role": "user", "content": f"{sender} sent: {content}"}
|
||||
]
|
||||
)
|
||||
reply = response.choices[0].message.content
|
||||
print(f"AI tra loi: {reply}")
|
||||
|
||||
time.sleep(1)
|
||||
pyautogui.click(x, y)
|
||||
time.sleep(1)
|
||||
for line in reply.split("\n"):
|
||||
pyautogui.write(line, interval=0.01)
|
||||
pyautogui.hotkey("shift", "enter")
|
||||
time.sleep(1)
|
||||
time.sleep(0.5)
|
||||
pyautogui.press("enter")
|
||||
pyautogui.press("enter")
|
||||
pyautogui.press("enter")
|
||||
|
||||
return reply
|
||||
except Exception as e:
|
||||
return f"Lỗi khi gọi AI: {str(e)}"
|
||||
|
||||
# API để nhận tin nhắn và phản hồi
|
||||
@app.post("/reply/")
|
||||
def reply_to_message(msg: Message):
|
||||
print(f"[{msg.room_name}] {msg.name}: {msg.message}")
|
||||
reply = generate_and_type_reply(msg.name, msg.message, msg.x, msg.y)
|
||||
return {
|
||||
"sender": msg.name,
|
||||
"message": msg.message,
|
||||
"room": msg.room_name,
|
||||
"reply": reply
|
||||
}
|
||||
|
||||
@app.post("/type/")
|
||||
def type(message: str = Body(..., embed=True)):
|
||||
try:
|
||||
print(f"Typing message: ")
|
||||
|
||||
pyautogui.click(x, y)
|
||||
time.sleep(1)
|
||||
|
||||
# Copy vào clipboard và dán
|
||||
pyperclip.copy(message)
|
||||
pyautogui.hotkey("ctrl", "v")
|
||||
time.sleep(0.5)
|
||||
|
||||
# Gửi Enter để gửi
|
||||
pyautogui.press("enter")
|
||||
|
||||
return {"status": "success", "typed": message}
|
||||
except Exception as e:
|
||||
return {"status": "error", "detail": str(e)}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
fastapi==0.111.0
|
||||
uvicorn==0.29.0
|
||||
pydantic==2.7.1
|
||||
openai==1.30.1
|
||||
pyautogui==0.9.54
|
||||
pymsgbox==1.0.9
|
||||
pytweening==1.0.7
|
||||
pygetwindow==0.0.9
|
||||
pyrect==0.2.0
|
||||
PyScreeze==0.1.30
|
||||
pyobjc-core==10.1
|
||||
pyobjc-framework-Quartz==10.1
|
||||
pyobjc-framework-Cocoa==10.1
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIID1TCCAr2gAwIBAgIUOWbfSNEe+a/qZo/1Y7DOFM6WZZ8wDQYJKoZIhvcNAQEL
|
||||
BQAwejELMAkGA1UEBhMCQVQxDjAMBgNVBAgMBUFkbWluMQ4wDAYDVQQHDAVBZG1p
|
||||
bjEOMAwGA1UECgwFQWRtaW4xCzAJBgNVBAsMAkFUMQ4wDAYDVQQDDAVBZG1pbjEe
|
||||
MBwGCSqGSIb3DQEJARYPYWRtaW5AZ21haWwuY29tMB4XDTI1MDgwNTAyMjMzOFoX
|
||||
DTI2MDgwNTAyMjMzOFowejELMAkGA1UEBhMCQVQxDjAMBgNVBAgMBUFkbWluMQ4w
|
||||
DAYDVQQHDAVBZG1pbjEOMAwGA1UECgwFQWRtaW4xCzAJBgNVBAsMAkFUMQ4wDAYD
|
||||
VQQDDAVBZG1pbjEeMBwGCSqGSIb3DQEJARYPYWRtaW5AZ21haWwuY29tMIIBIjAN
|
||||
BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtK8sAFtijD7IG5UP7DIE1zaGUUch
|
||||
YH9ubXOb0LX5tqb3asLVqFehveRUVolUoG3t+wM8yaxF/MBk4vkEZoEbhQiKGDfV
|
||||
37FakHcw74cLEPgMuVGU91/yt3ca8+6zSSYcsIpJaWIWFNlGMnk3TIs4HRnM80rY
|
||||
1R6q94SP3Yx2/DlcKoYxb/onnRs8pTJZPIw+YdRm1Yput6EO1WTjinG9JmAbeNsS
|
||||
f643nN0oQ5JYf0JTQXi/8iLJExwJwbl+97aS2+l/+tSujqIkLUE1ighxGk7ZxZfO
|
||||
3V9gd5kdSYbsMhQqDdEUjmmNqmAQ6F8MRkPu8+/jAfQH73+rzQBrqZMe+QIDAQAB
|
||||
o1MwUTAdBgNVHQ4EFgQUSmsyedYpQW1A9z0IVbQ4wR2gDcYwHwYDVR0jBBgwFoAU
|
||||
SmsyedYpQW1A9z0IVbQ4wR2gDcYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0B
|
||||
AQsFAAOCAQEAZd0OO2r/vs+56PhDLnB9fMiA8WSD9d2J8VNaA1IYCRkEQwEHlFUQ
|
||||
CSu9ZIJtU+UXcDpnjzHc7hV1yYufhMTKUPWY9rlVypWoCoOyMw7Z9O0Tk+je4g2i
|
||||
ChV2HSAYFSAhJrt1zNUofNBjXjgX08Mr65LYo3eIj9Pl8dmfun5wjDAlNv6uj9+F
|
||||
eJzlsnCa2/fDn6zDGzKALXtNOPA/jNoKVP5uGbgCxaUNFA5dZvA5MBg8jXYC2r/Z
|
||||
bL47zml8alx+HOgxJkkeQqx5rjBw+j2fNK7h3ot6ME7Gu9+MDgg2n1rKiJqrWCTH
|
||||
oi0vQprP9ccgTHQB5cHTEQsOQhYcOjq9ug==
|
||||
-----END CERTIFICATE-----
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC0rywAW2KMPsgb
|
||||
lQ/sMgTXNoZRRyFgf25tc5vQtfm2pvdqwtWoV6G95FRWiVSgbe37AzzJrEX8wGTi
|
||||
+QRmgRuFCIoYN9XfsVqQdzDvhwsQ+Ay5UZT3X/K3dxrz7rNJJhywiklpYhYU2UYy
|
||||
eTdMizgdGczzStjVHqr3hI/djHb8OVwqhjFv+iedGzylMlk8jD5h1GbVim63oQ7V
|
||||
ZOOKcb0mYBt42xJ/rjec3ShDklh/QlNBeL/yIskTHAnBuX73tpLb6X/61K6OoiQt
|
||||
QTWKCHEaTtnFl87dX2B3mR1JhuwyFCoN0RSOaY2qYBDoXwxGQ+7z7+MB9Afvf6vN
|
||||
AGupkx75AgMBAAECggEAAlrmqUxuCUf8gnyG+1QiRnfzcf3p+28JyANjegNtmvHl
|
||||
iqN5vRxQTPbCQSlm1c6Wm6JM2AMzqDunQC/QybFZqk2EhEdYO+NQFYI4nD4/vw1Z
|
||||
2Bs68USaEXlfIiPf1YTs4PTnR+hjab5OKSecK7zRHH/K6uoBHJNuFQFKGtzv95gv
|
||||
r0ECwJoZo+pWswYmUiHeaBLrxdAq4LNdKVLKcZ3Pi6NU3pSQynGRLkjTC1PqSiUm
|
||||
KAWHFEpt6Co5AVDkgBc5TNIex90khKZIRciFNL8VjR1su3EJZ2m71cqagvMhBVz1
|
||||
g4wHY7FR6liqiC9NuFT2FfVer56sX5+GZtw21J9lUQKBgQDm6avJ0NXplo7Lhta4
|
||||
4CI2nn1jdk+7kGVBCHyRevdtFrcXsO8rbpv8cyuZy+tNGtPg995pJYkE55uv11T1
|
||||
Ar8Qk0SbpMefexv33XcX5/wIGB34xgZ31XoUAYAkpFi7iY2hOQgpTaftOJ5D3UHj
|
||||
k9OHk+zRAclv8nvvrGY6KwENqQKBgQDIUIF0sRHQWbulSGOo2PeXO5p9Gh/brEg2
|
||||
/orfftj5BmY4Rmin6ZPeShh9huVxCwId3+DPZxYulyeu0BLLXloZ5Z8ZnDkiG9Ux
|
||||
okm1691BUqJTguKE3EuB5yZ6kOT8QL/9yifXYn+O8BDKN/V4s+nDop2e13GRrGoM
|
||||
SHS0jSo40QKBgC0IUy1XofAdIZoFoOiLJYD7Zb64+xG8NlL5gbmpfolyzaA8LFiP
|
||||
CLKyD0W+JBR0b+/Gx0RAQrmHJbCkUPg7YeGspsun1hp/GtQ7B5fM8TTu5yF2FRrX
|
||||
sigmpsP+nSNtzwYrJjFP6Lj1ur/HbashUDZ3nc6hB5TieFMkjwc3DcyJAoGBAK7m
|
||||
TAIdURaAIu0PpaSZvKSZcCxnEQX3CR6rZYn68WuNgNcF8v8ZXPir7XI8xDzNhc2b
|
||||
2mh888s+Q3HJT3+uJDGUYjQ0SVZUwvMRZhanmIoeookUMMAcsPj9YIWH2ce+qWPo
|
||||
jJzs7b9aMO6/qV74h9U/OSylpA2zYuzSnsO+tezhAoGBAIDenq6wO29gCbIdiOoU
|
||||
5AAKaIfHFSqr62eLjy/1ZhJ8rsmi75Du4MMwEsCJN/btx68SHnvdk1EL+sEJaUp1
|
||||
cUgX4inKRajuQtVQ5BFtH0/r5g5gXgCtlloz+1jyQi+i+YidW8tW+88kXCjO1ZiS
|
||||
JWLFknfFx8L1xUBW3R1Hadvz
|
||||
-----END PRIVATE KEY-----
|
||||
|
|
@ -0,0 +1,247 @@
|
|||
<#
|
||||
.Synopsis
|
||||
Activate a Python virtual environment for the current PowerShell session.
|
||||
|
||||
.Description
|
||||
Pushes the python executable for a virtual environment to the front of the
|
||||
$Env:PATH environment variable and sets the prompt to signify that you are
|
||||
in a Python virtual environment. Makes use of the command line switches as
|
||||
well as the `pyvenv.cfg` file values present in the virtual environment.
|
||||
|
||||
.Parameter VenvDir
|
||||
Path to the directory that contains the virtual environment to activate. The
|
||||
default value for this is the parent of the directory that the Activate.ps1
|
||||
script is located within.
|
||||
|
||||
.Parameter Prompt
|
||||
The prompt prefix to display when this virtual environment is activated. By
|
||||
default, this prompt is the name of the virtual environment folder (VenvDir)
|
||||
surrounded by parentheses and followed by a single space (ie. '(.venv) ').
|
||||
|
||||
.Example
|
||||
Activate.ps1
|
||||
Activates the Python virtual environment that contains the Activate.ps1 script.
|
||||
|
||||
.Example
|
||||
Activate.ps1 -Verbose
|
||||
Activates the Python virtual environment that contains the Activate.ps1 script,
|
||||
and shows extra information about the activation as it executes.
|
||||
|
||||
.Example
|
||||
Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv
|
||||
Activates the Python virtual environment located in the specified location.
|
||||
|
||||
.Example
|
||||
Activate.ps1 -Prompt "MyPython"
|
||||
Activates the Python virtual environment that contains the Activate.ps1 script,
|
||||
and prefixes the current prompt with the specified string (surrounded in
|
||||
parentheses) while the virtual environment is active.
|
||||
|
||||
.Notes
|
||||
On Windows, it may be required to enable this Activate.ps1 script by setting the
|
||||
execution policy for the user. You can do this by issuing the following PowerShell
|
||||
command:
|
||||
|
||||
PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
||||
|
||||
For more information on Execution Policies:
|
||||
https://go.microsoft.com/fwlink/?LinkID=135170
|
||||
|
||||
#>
|
||||
Param(
|
||||
[Parameter(Mandatory = $false)]
|
||||
[String]
|
||||
$VenvDir,
|
||||
[Parameter(Mandatory = $false)]
|
||||
[String]
|
||||
$Prompt
|
||||
)
|
||||
|
||||
<# Function declarations --------------------------------------------------- #>
|
||||
|
||||
<#
|
||||
.Synopsis
|
||||
Remove all shell session elements added by the Activate script, including the
|
||||
addition of the virtual environment's Python executable from the beginning of
|
||||
the PATH variable.
|
||||
|
||||
.Parameter NonDestructive
|
||||
If present, do not remove this function from the global namespace for the
|
||||
session.
|
||||
|
||||
#>
|
||||
function global:deactivate ([switch]$NonDestructive) {
|
||||
# Revert to original values
|
||||
|
||||
# The prior prompt:
|
||||
if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) {
|
||||
Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt
|
||||
Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT
|
||||
}
|
||||
|
||||
# The prior PYTHONHOME:
|
||||
if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) {
|
||||
Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME
|
||||
Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME
|
||||
}
|
||||
|
||||
# The prior PATH:
|
||||
if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) {
|
||||
Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH
|
||||
Remove-Item -Path Env:_OLD_VIRTUAL_PATH
|
||||
}
|
||||
|
||||
# Just remove the VIRTUAL_ENV altogether:
|
||||
if (Test-Path -Path Env:VIRTUAL_ENV) {
|
||||
Remove-Item -Path env:VIRTUAL_ENV
|
||||
}
|
||||
|
||||
# Just remove VIRTUAL_ENV_PROMPT altogether.
|
||||
if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) {
|
||||
Remove-Item -Path env:VIRTUAL_ENV_PROMPT
|
||||
}
|
||||
|
||||
# Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether:
|
||||
if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) {
|
||||
Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force
|
||||
}
|
||||
|
||||
# Leave deactivate function in the global namespace if requested:
|
||||
if (-not $NonDestructive) {
|
||||
Remove-Item -Path function:deactivate
|
||||
}
|
||||
}
|
||||
|
||||
<#
|
||||
.Description
|
||||
Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the
|
||||
given folder, and returns them in a map.
|
||||
|
||||
For each line in the pyvenv.cfg file, if that line can be parsed into exactly
|
||||
two strings separated by `=` (with any amount of whitespace surrounding the =)
|
||||
then it is considered a `key = value` line. The left hand string is the key,
|
||||
the right hand is the value.
|
||||
|
||||
If the value starts with a `'` or a `"` then the first and last character is
|
||||
stripped from the value before being captured.
|
||||
|
||||
.Parameter ConfigDir
|
||||
Path to the directory that contains the `pyvenv.cfg` file.
|
||||
#>
|
||||
function Get-PyVenvConfig(
|
||||
[String]
|
||||
$ConfigDir
|
||||
) {
|
||||
Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg"
|
||||
|
||||
# Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue).
|
||||
$pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue
|
||||
|
||||
# An empty map will be returned if no config file is found.
|
||||
$pyvenvConfig = @{ }
|
||||
|
||||
if ($pyvenvConfigPath) {
|
||||
|
||||
Write-Verbose "File exists, parse `key = value` lines"
|
||||
$pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath
|
||||
|
||||
$pyvenvConfigContent | ForEach-Object {
|
||||
$keyval = $PSItem -split "\s*=\s*", 2
|
||||
if ($keyval[0] -and $keyval[1]) {
|
||||
$val = $keyval[1]
|
||||
|
||||
# Remove extraneous quotations around a string value.
|
||||
if ("'""".Contains($val.Substring(0, 1))) {
|
||||
$val = $val.Substring(1, $val.Length - 2)
|
||||
}
|
||||
|
||||
$pyvenvConfig[$keyval[0]] = $val
|
||||
Write-Verbose "Adding Key: '$($keyval[0])'='$val'"
|
||||
}
|
||||
}
|
||||
}
|
||||
return $pyvenvConfig
|
||||
}
|
||||
|
||||
|
||||
<# Begin Activate script --------------------------------------------------- #>
|
||||
|
||||
# Determine the containing directory of this script
|
||||
$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
|
||||
$VenvExecDir = Get-Item -Path $VenvExecPath
|
||||
|
||||
Write-Verbose "Activation script is located in path: '$VenvExecPath'"
|
||||
Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)"
|
||||
Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)"
|
||||
|
||||
# Set values required in priority: CmdLine, ConfigFile, Default
|
||||
# First, get the location of the virtual environment, it might not be
|
||||
# VenvExecDir if specified on the command line.
|
||||
if ($VenvDir) {
|
||||
Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values"
|
||||
}
|
||||
else {
|
||||
Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir."
|
||||
$VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/")
|
||||
Write-Verbose "VenvDir=$VenvDir"
|
||||
}
|
||||
|
||||
# Next, read the `pyvenv.cfg` file to determine any required value such
|
||||
# as `prompt`.
|
||||
$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir
|
||||
|
||||
# Next, set the prompt from the command line, or the config file, or
|
||||
# just use the name of the virtual environment folder.
|
||||
if ($Prompt) {
|
||||
Write-Verbose "Prompt specified as argument, using '$Prompt'"
|
||||
}
|
||||
else {
|
||||
Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value"
|
||||
if ($pyvenvCfg -and $pyvenvCfg['prompt']) {
|
||||
Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'"
|
||||
$Prompt = $pyvenvCfg['prompt'];
|
||||
}
|
||||
else {
|
||||
Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)"
|
||||
Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'"
|
||||
$Prompt = Split-Path -Path $venvDir -Leaf
|
||||
}
|
||||
}
|
||||
|
||||
Write-Verbose "Prompt = '$Prompt'"
|
||||
Write-Verbose "VenvDir='$VenvDir'"
|
||||
|
||||
# Deactivate any currently active virtual environment, but leave the
|
||||
# deactivate function in place.
|
||||
deactivate -nondestructive
|
||||
|
||||
# Now set the environment variable VIRTUAL_ENV, used by many tools to determine
|
||||
# that there is an activated venv.
|
||||
$env:VIRTUAL_ENV = $VenvDir
|
||||
|
||||
if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) {
|
||||
|
||||
Write-Verbose "Setting prompt to '$Prompt'"
|
||||
|
||||
# Set the prompt to include the env name
|
||||
# Make sure _OLD_VIRTUAL_PROMPT is global
|
||||
function global:_OLD_VIRTUAL_PROMPT { "" }
|
||||
Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT
|
||||
New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt
|
||||
|
||||
function global:prompt {
|
||||
Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) "
|
||||
_OLD_VIRTUAL_PROMPT
|
||||
}
|
||||
$env:VIRTUAL_ENV_PROMPT = $Prompt
|
||||
}
|
||||
|
||||
# Clear PYTHONHOME
|
||||
if (Test-Path -Path Env:PYTHONHOME) {
|
||||
Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME
|
||||
Remove-Item -Path Env:PYTHONHOME
|
||||
}
|
||||
|
||||
# Add the venv to the PATH
|
||||
Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH
|
||||
$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH"
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
# This file must be used with "source bin/activate" *from bash*
|
||||
# you cannot run it directly
|
||||
|
||||
deactivate () {
|
||||
# reset old environment variables
|
||||
if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
|
||||
PATH="${_OLD_VIRTUAL_PATH:-}"
|
||||
export PATH
|
||||
unset _OLD_VIRTUAL_PATH
|
||||
fi
|
||||
if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
|
||||
PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
|
||||
export PYTHONHOME
|
||||
unset _OLD_VIRTUAL_PYTHONHOME
|
||||
fi
|
||||
|
||||
# This should detect bash and zsh, which have a hash command that must
|
||||
# be called to get it to forget past commands. Without forgetting
|
||||
# past commands the $PATH changes we made may not be respected
|
||||
if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
|
||||
hash -r 2> /dev/null
|
||||
fi
|
||||
|
||||
if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
|
||||
PS1="${_OLD_VIRTUAL_PS1:-}"
|
||||
export PS1
|
||||
unset _OLD_VIRTUAL_PS1
|
||||
fi
|
||||
|
||||
unset VIRTUAL_ENV
|
||||
unset VIRTUAL_ENV_PROMPT
|
||||
if [ ! "${1:-}" = "nondestructive" ] ; then
|
||||
# Self destruct!
|
||||
unset -f deactivate
|
||||
fi
|
||||
}
|
||||
|
||||
# unset irrelevant variables
|
||||
deactivate nondestructive
|
||||
|
||||
VIRTUAL_ENV="/Users/admin/Workspace/do-something/teams-bots/write-message/venv"
|
||||
export VIRTUAL_ENV
|
||||
|
||||
_OLD_VIRTUAL_PATH="$PATH"
|
||||
PATH="$VIRTUAL_ENV/bin:$PATH"
|
||||
export PATH
|
||||
|
||||
# unset PYTHONHOME if set
|
||||
# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
|
||||
# could use `if (set -u; : $PYTHONHOME) ;` in bash
|
||||
if [ -n "${PYTHONHOME:-}" ] ; then
|
||||
_OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
|
||||
unset PYTHONHOME
|
||||
fi
|
||||
|
||||
if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
|
||||
_OLD_VIRTUAL_PS1="${PS1:-}"
|
||||
PS1="(venv) ${PS1:-}"
|
||||
export PS1
|
||||
VIRTUAL_ENV_PROMPT="(venv) "
|
||||
export VIRTUAL_ENV_PROMPT
|
||||
fi
|
||||
|
||||
# This should detect bash and zsh, which have a hash command that must
|
||||
# be called to get it to forget past commands. Without forgetting
|
||||
# past commands the $PATH changes we made may not be respected
|
||||
if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
|
||||
hash -r 2> /dev/null
|
||||
fi
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
# This file must be used with "source bin/activate.csh" *from csh*.
|
||||
# You cannot run it directly.
|
||||
# Created by Davide Di Blasi <davidedb@gmail.com>.
|
||||
# Ported to Python 3.3 venv by Andrew Svetlov <andrew.svetlov@gmail.com>
|
||||
|
||||
alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate'
|
||||
|
||||
# Unset irrelevant variables.
|
||||
deactivate nondestructive
|
||||
|
||||
setenv VIRTUAL_ENV "/Users/admin/Workspace/do-something/teams-bots/write-message/venv"
|
||||
|
||||
set _OLD_VIRTUAL_PATH="$PATH"
|
||||
setenv PATH "$VIRTUAL_ENV/bin:$PATH"
|
||||
|
||||
|
||||
set _OLD_VIRTUAL_PROMPT="$prompt"
|
||||
|
||||
if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then
|
||||
set prompt = "(venv) $prompt"
|
||||
setenv VIRTUAL_ENV_PROMPT "(venv) "
|
||||
endif
|
||||
|
||||
alias pydoc python -m pydoc
|
||||
|
||||
rehash
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
# This file must be used with "source <venv>/bin/activate.fish" *from fish*
|
||||
# (https://fishshell.com/); you cannot run it directly.
|
||||
|
||||
function deactivate -d "Exit virtual environment and return to normal shell environment"
|
||||
# reset old environment variables
|
||||
if test -n "$_OLD_VIRTUAL_PATH"
|
||||
set -gx PATH $_OLD_VIRTUAL_PATH
|
||||
set -e _OLD_VIRTUAL_PATH
|
||||
end
|
||||
if test -n "$_OLD_VIRTUAL_PYTHONHOME"
|
||||
set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME
|
||||
set -e _OLD_VIRTUAL_PYTHONHOME
|
||||
end
|
||||
|
||||
if test -n "$_OLD_FISH_PROMPT_OVERRIDE"
|
||||
set -e _OLD_FISH_PROMPT_OVERRIDE
|
||||
# prevents error when using nested fish instances (Issue #93858)
|
||||
if functions -q _old_fish_prompt
|
||||
functions -e fish_prompt
|
||||
functions -c _old_fish_prompt fish_prompt
|
||||
functions -e _old_fish_prompt
|
||||
end
|
||||
end
|
||||
|
||||
set -e VIRTUAL_ENV
|
||||
set -e VIRTUAL_ENV_PROMPT
|
||||
if test "$argv[1]" != "nondestructive"
|
||||
# Self-destruct!
|
||||
functions -e deactivate
|
||||
end
|
||||
end
|
||||
|
||||
# Unset irrelevant variables.
|
||||
deactivate nondestructive
|
||||
|
||||
set -gx VIRTUAL_ENV "/Users/admin/Workspace/do-something/teams-bots/write-message/venv"
|
||||
|
||||
set -gx _OLD_VIRTUAL_PATH $PATH
|
||||
set -gx PATH "$VIRTUAL_ENV/bin" $PATH
|
||||
|
||||
# Unset PYTHONHOME if set.
|
||||
if set -q PYTHONHOME
|
||||
set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME
|
||||
set -e PYTHONHOME
|
||||
end
|
||||
|
||||
if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
|
||||
# fish uses a function instead of an env var to generate the prompt.
|
||||
|
||||
# Save the current fish_prompt function as the function _old_fish_prompt.
|
||||
functions -c fish_prompt _old_fish_prompt
|
||||
|
||||
# With the original prompt function renamed, we can override with our own.
|
||||
function fish_prompt
|
||||
# Save the return status of the last command.
|
||||
set -l old_status $status
|
||||
|
||||
# Output the venv prompt; color taken from the blue of the Python logo.
|
||||
printf "%s%s%s" (set_color 4B8BBE) "(venv) " (set_color normal)
|
||||
|
||||
# Restore the return status of the previous command.
|
||||
echo "exit $old_status" | .
|
||||
# Output the original/"old" prompt.
|
||||
_old_fish_prompt
|
||||
end
|
||||
|
||||
set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV"
|
||||
set -gx VIRTUAL_ENV_PROMPT "(venv) "
|
||||
end
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#!/Users/admin/Workspace/do-something/teams-bots/write-message/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from distro.distro import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#!/Users/admin/Workspace/do-something/teams-bots/write-message/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from dotenv.__main__ import cli
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(cli())
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#!/Users/admin/Workspace/do-something/teams-bots/write-message/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from email_validator.__main__ import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#!/Users/admin/Workspace/do-something/teams-bots/write-message/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from fastapi_cli.cli import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#!/Users/admin/Workspace/do-something/teams-bots/write-message/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from httpx import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#!/Users/admin/Workspace/do-something/teams-bots/write-message/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from markdown_it.cli.parse import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#!/Users/admin/Workspace/do-something/teams-bots/write-message/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from openai.cli import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#!/Users/admin/Workspace/do-something/teams-bots/write-message/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from pip._internal.cli.main import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#!/Users/admin/Workspace/do-something/teams-bots/write-message/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from pip._internal.cli.main import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#!/Users/admin/Workspace/do-something/teams-bots/write-message/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from pip._internal.cli.main import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#!/Users/admin/Workspace/do-something/teams-bots/write-message/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from pygments.cmdline import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
|
|
@ -0,0 +1 @@
|
|||
python3
|
||||
|
|
@ -0,0 +1 @@
|
|||
/Users/admin/.pyenv/versions/3.10.13/bin/python3
|
||||
|
|
@ -0,0 +1 @@
|
|||
python3
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#!/Users/admin/Workspace/do-something/teams-bots/write-message/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from tqdm.cli import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#!/Users/admin/Workspace/do-something/teams-bots/write-message/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from typer.cli import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#!/Users/admin/Workspace/do-something/teams-bots/write-message/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from uvicorn.main import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#!/Users/admin/Workspace/do-something/teams-bots/write-message/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from watchfiles.cli import cli
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(cli())
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
#!/Users/admin/Workspace/do-something/teams-bots/write-message/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from websockets.cli import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
Binary file not shown.
|
|
@ -0,0 +1,159 @@
|
|||
"""
|
||||
Python mapping for the AppKit framework.
|
||||
|
||||
This module does not contain docstrings for the wrapped code, check Apple's
|
||||
documentation for details on how to use these functions and classes.
|
||||
"""
|
||||
|
||||
|
||||
def _setup():
|
||||
import sys
|
||||
|
||||
import Foundation
|
||||
import objc
|
||||
from . import _metadata, _nsapp, _AppKit
|
||||
from ._inlines import _inline_list_
|
||||
|
||||
dir_func, getattr_func = objc.createFrameworkDirAndGetattr(
|
||||
name="AppKit",
|
||||
frameworkIdentifier="com.apple.AppKit",
|
||||
frameworkPath=objc.pathForFramework(
|
||||
"/System/Library/Frameworks/AppKit.framework"
|
||||
),
|
||||
globals_dict=globals(),
|
||||
inline_list=_inline_list_,
|
||||
parents=(
|
||||
_nsapp,
|
||||
_AppKit,
|
||||
Foundation,
|
||||
),
|
||||
metadict=_metadata.__dict__,
|
||||
)
|
||||
|
||||
globals()["__dir__"] = dir_func
|
||||
globals()["__getattr__"] = getattr_func
|
||||
|
||||
del sys.modules["AppKit._metadata"]
|
||||
|
||||
def fontdescriptor_get(self, key, default=None):
|
||||
value = self.objectForKey_(key)
|
||||
if value is None:
|
||||
return default
|
||||
return value
|
||||
|
||||
def fontdescriptor_getitem(self, key, default=None):
|
||||
value = self.objectForKey_(key)
|
||||
if value is None:
|
||||
raise KeyError(key)
|
||||
return value
|
||||
|
||||
objc.addConvenienceForClass(
|
||||
"NSFontDescriptor",
|
||||
(("__getitem__", fontdescriptor_getitem), ("get", fontdescriptor_get)),
|
||||
)
|
||||
|
||||
# Fix types for a number of character constants
|
||||
# XXX: Move this to metadata
|
||||
globals_dict = globals()
|
||||
for nm in [
|
||||
"NSEnterCharacter",
|
||||
"NSBackspaceCharacter",
|
||||
"NSTabCharacter",
|
||||
"NSNewlineCharacter",
|
||||
"NSFormFeedCharacter",
|
||||
"NSCarriageReturnCharacter",
|
||||
"NSBackTabCharacter",
|
||||
"NSDeleteCharacter",
|
||||
"NSLineSeparatorCharacter",
|
||||
"NSParagraphSeparatorCharacter",
|
||||
"NSUpArrowFunctionKey",
|
||||
"NSDownArrowFunctionKey",
|
||||
"NSLeftArrowFunctionKey",
|
||||
"NSRightArrowFunctionKey",
|
||||
"NSF1FunctionKey",
|
||||
"NSF2FunctionKey",
|
||||
"NSF3FunctionKey",
|
||||
"NSF4FunctionKey",
|
||||
"NSF5FunctionKey",
|
||||
"NSF6FunctionKey",
|
||||
"NSF7FunctionKey",
|
||||
"NSF8FunctionKey",
|
||||
"NSF9FunctionKey",
|
||||
"NSF10FunctionKey",
|
||||
"NSF11FunctionKey",
|
||||
"NSF12FunctionKey",
|
||||
"NSF13FunctionKey",
|
||||
"NSF14FunctionKey",
|
||||
"NSF15FunctionKey",
|
||||
"NSF16FunctionKey",
|
||||
"NSF17FunctionKey",
|
||||
"NSF18FunctionKey",
|
||||
"NSF19FunctionKey",
|
||||
"NSF20FunctionKey",
|
||||
"NSF21FunctionKey",
|
||||
"NSF22FunctionKey",
|
||||
"NSF23FunctionKey",
|
||||
"NSF24FunctionKey",
|
||||
"NSF25FunctionKey",
|
||||
"NSF26FunctionKey",
|
||||
"NSF27FunctionKey",
|
||||
"NSF28FunctionKey",
|
||||
"NSF29FunctionKey",
|
||||
"NSF30FunctionKey",
|
||||
"NSF31FunctionKey",
|
||||
"NSF32FunctionKey",
|
||||
"NSF33FunctionKey",
|
||||
"NSF34FunctionKey",
|
||||
"NSF35FunctionKey",
|
||||
"NSInsertFunctionKey",
|
||||
"NSDeleteFunctionKey",
|
||||
"NSHomeFunctionKey",
|
||||
"NSBeginFunctionKey",
|
||||
"NSEndFunctionKey",
|
||||
"NSPageUpFunctionKey",
|
||||
"NSPageDownFunctionKey",
|
||||
"NSPrintScreenFunctionKey",
|
||||
"NSScrollLockFunctionKey",
|
||||
"NSPauseFunctionKey",
|
||||
"NSSysReqFunctionKey",
|
||||
"NSBreakFunctionKey",
|
||||
"NSResetFunctionKey",
|
||||
"NSStopFunctionKey",
|
||||
"NSMenuFunctionKey",
|
||||
"NSUserFunctionKey",
|
||||
"NSSystemFunctionKey",
|
||||
"NSPrintFunctionKey",
|
||||
"NSClearLineFunctionKey",
|
||||
"NSClearDisplayFunctionKey",
|
||||
"NSInsertLineFunctionKey",
|
||||
"NSDeleteLineFunctionKey",
|
||||
"NSInsertCharFunctionKey",
|
||||
"NSDeleteCharFunctionKey",
|
||||
"NSPrevFunctionKey",
|
||||
"NSNextFunctionKey",
|
||||
"NSSelectFunctionKey",
|
||||
"NSExecuteFunctionKey",
|
||||
"NSUndoFunctionKey",
|
||||
"NSRedoFunctionKey",
|
||||
"NSFindFunctionKey",
|
||||
"NSHelpFunctionKey",
|
||||
"NSModeSwitchFunctionKey",
|
||||
]:
|
||||
try:
|
||||
globals_dict[nm] = chr(__getattr__(nm)) # noqa: F821
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
|
||||
globals().pop("_setup")()
|
||||
|
||||
|
||||
def NSDictionaryOfVariableBindings(*names):
|
||||
"""
|
||||
Return a dictionary with the given names and there values.
|
||||
"""
|
||||
import sys
|
||||
|
||||
variables = sys._getframe(1).f_locals
|
||||
|
||||
return {nm: variables[nm] for nm in names}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,29 @@
|
|||
import objc as _objc
|
||||
import AppKit as _AppKit
|
||||
|
||||
|
||||
class _NSApp:
|
||||
"""
|
||||
Helper class to emulate NSApp in Python.
|
||||
"""
|
||||
|
||||
def __getrealapp(self):
|
||||
d = {}
|
||||
_objc.loadBundleVariables(_AppKit.__bundle__, d, [("NSApp", b"@")])
|
||||
return d.get("NSApp")
|
||||
|
||||
__class__ = property(lambda self: self.__getrealapp().__class__)
|
||||
|
||||
def __getattr__(self, name):
|
||||
return getattr(self.__getrealapp(), name)
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
return setattr(self.__getrealapp(), name, value)
|
||||
|
||||
def __call__(self):
|
||||
# Compatibility with previous versions.
|
||||
return self.__getrealapp()
|
||||
|
||||
|
||||
NSApp = _NSApp()
|
||||
del _NSApp
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
"""
|
||||
Python mapping for the Cocoa framework.
|
||||
|
||||
This module does not contain docstrings for the wrapped code, check Apple's
|
||||
documentation for details on how to use these functions and classes.
|
||||
"""
|
||||
|
||||
|
||||
def _setup():
|
||||
import AppKit
|
||||
import objc
|
||||
|
||||
dir_func, getattr_func = objc.createFrameworkDirAndGetattr(
|
||||
name="Cocoa",
|
||||
frameworkIdentifier=None,
|
||||
frameworkPath=None,
|
||||
globals_dict=globals(),
|
||||
inline_list=None,
|
||||
parents=(AppKit,),
|
||||
metadict={},
|
||||
)
|
||||
|
||||
globals()["__dir__"] = dir_func
|
||||
globals()["__getattr__"] = getattr_func
|
||||
|
||||
|
||||
globals().pop("_setup")()
|
||||
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,37 @@
|
|||
"""
|
||||
Python mapping for the CoreFoundation framework.
|
||||
|
||||
This module does not contain docstrings for the wrapped code, check Apple's
|
||||
documentation for details on how to use these functions and classes.
|
||||
"""
|
||||
|
||||
|
||||
def _setup():
|
||||
import sys
|
||||
|
||||
import objc
|
||||
from . import _metadata, _CoreFoundation, _static
|
||||
from ._inlines import _inline_list_
|
||||
|
||||
dir_func, getattr_func = objc.createFrameworkDirAndGetattr(
|
||||
name="CoreFoundation",
|
||||
frameworkIdentifier="com.apple.CoreFoundation",
|
||||
frameworkPath=objc.pathForFramework(
|
||||
"/System/Library/Frameworks/CoreFoundation.framework"
|
||||
),
|
||||
globals_dict=globals(),
|
||||
inline_list=_inline_list_,
|
||||
parents=(
|
||||
_CoreFoundation,
|
||||
_static,
|
||||
),
|
||||
metadict=_metadata.__dict__,
|
||||
)
|
||||
|
||||
globals()["__dir__"] = dir_func
|
||||
globals()["__getattr__"] = getattr_func
|
||||
|
||||
del sys.modules["CoreFoundation._metadata"]
|
||||
|
||||
|
||||
globals().pop("_setup")()
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,107 @@
|
|||
import CoreFoundation as _CF
|
||||
import objc as _objc
|
||||
|
||||
|
||||
#
|
||||
# 'Emulation' for CFArray constructors
|
||||
#
|
||||
def _setup():
|
||||
NSArray = _objc.lookUpClass("NSArray")
|
||||
NSMutableArray = _objc.lookUpClass("NSMutableArray")
|
||||
|
||||
def CFArrayCreate(allocator, values, numvalues, callbacks):
|
||||
assert callbacks is None
|
||||
return NSArray.alloc().initWithArray_(values[:numvalues])
|
||||
|
||||
def CFArrayCreateMutable(allocator, capacity, callbacks):
|
||||
assert callbacks is None
|
||||
return NSMutableArray.alloc().init()
|
||||
|
||||
return CFArrayCreate, CFArrayCreateMutable
|
||||
|
||||
|
||||
CFArrayCreate, CFArrayCreateMutable = _setup()
|
||||
|
||||
# CFDictionary emulation functions
|
||||
|
||||
|
||||
def _setup():
|
||||
NSDictionary = _objc.lookUpClass("NSDictionary")
|
||||
NSMutableDictionary = _objc.lookUpClass("NSMutableDictionary")
|
||||
|
||||
def CFDictionaryCreate(
|
||||
allocator, keys, values, numValues, keyCallbacks, valueCallbacks
|
||||
):
|
||||
assert keyCallbacks is None
|
||||
assert valueCallbacks is None
|
||||
|
||||
keys = list(keys)[:numValues]
|
||||
values = list(values)[:numValues]
|
||||
|
||||
return NSDictionary.dictionaryWithDictionary_(dict(zip(keys, values)))
|
||||
|
||||
def CFDictionaryCreateMutable(allocator, capacity, keyCallbacks, valueCallbacks):
|
||||
assert keyCallbacks is None
|
||||
assert valueCallbacks is None
|
||||
|
||||
return NSMutableDictionary.dictionary()
|
||||
|
||||
return CFDictionaryCreate, CFDictionaryCreateMutable
|
||||
|
||||
|
||||
CFDictionaryCreate, CFDictionaryCreateMutable = _setup()
|
||||
|
||||
|
||||
# CFSet emulation functions
|
||||
|
||||
|
||||
def _setup():
|
||||
NSSet = _objc.lookUpClass("NSSet")
|
||||
NSMutableSet = _objc.lookUpClass("NSMutableSet")
|
||||
|
||||
def CFSetCreate(allocator, values, numvalues, callbacks):
|
||||
assert callbacks is None
|
||||
return NSSet.alloc().initWithArray_(values[:numvalues])
|
||||
|
||||
def CFSetCreateMutable(allocator, capacity, callbacks):
|
||||
assert callbacks is None
|
||||
return NSMutableSet.alloc().init()
|
||||
|
||||
return CFSetCreate, CFSetCreateMutable
|
||||
|
||||
|
||||
CFSetCreate, CFSetCreateMutable = _setup()
|
||||
|
||||
kCFTypeArrayCallBacks = None
|
||||
kCFTypeDictionaryKeyCallBacks = None
|
||||
kCFTypeDictionaryValueCallBacks = None
|
||||
kCFTypeSetCallBacks = None
|
||||
|
||||
|
||||
#
|
||||
# Implementation of a number of macro's in the CFBundle API
|
||||
#
|
||||
|
||||
|
||||
def CFCopyLocalizedString(key, comment):
|
||||
return _CF.CFBundleCopyLocalizedString(
|
||||
_CF.CFBundleGetMainBundle(), (key), (key), None
|
||||
)
|
||||
|
||||
|
||||
def CFCopyLocalizedStringFromTable(key, tbl, comment):
|
||||
return _CF.CFBundleCopyLocalizedString(
|
||||
_CF.CFBundleGetMainBundle(), (key), (key), (tbl)
|
||||
)
|
||||
|
||||
|
||||
def CFCopyLocalizedStringFromTableInBundle(key, tbl, bundle, comment):
|
||||
return _CF.CFBundleCopyLocalizedString((bundle), (key), (key), (tbl))
|
||||
|
||||
|
||||
def CFCopyLocalizedStringWithDefaultValue(key, tbl, bundle, value, comment):
|
||||
return _CF.CFBundleCopyLocalizedString((bundle), (key), (value), (tbl))
|
||||
|
||||
|
||||
def CFSTR(strval):
|
||||
return _objc.lookUpClass("NSString").stringWithString_(strval)
|
||||
Binary file not shown.
|
|
@ -0,0 +1,196 @@
|
|||
"""
|
||||
Python mapping for the Foundation framework.
|
||||
|
||||
This module does not contain docstrings for the wrapped code, check Apple's
|
||||
documentation for details on how to use these functions and classes.
|
||||
"""
|
||||
|
||||
|
||||
def _setup():
|
||||
import sys
|
||||
|
||||
import CoreFoundation
|
||||
import objc
|
||||
from . import _Foundation, _metadata, _functiondefines, _context
|
||||
from ._inlines import _inline_list_
|
||||
|
||||
dir_func, getattr_func = objc.createFrameworkDirAndGetattr(
|
||||
name="Foundation",
|
||||
frameworkIdentifier="com.apple.Foundation",
|
||||
frameworkPath=objc.pathForFramework(
|
||||
"/System/Library/Frameworks/Foundation.framework"
|
||||
),
|
||||
globals_dict=globals(),
|
||||
inline_list=_inline_list_,
|
||||
parents=(
|
||||
_Foundation,
|
||||
_functiondefines,
|
||||
_context,
|
||||
CoreFoundation,
|
||||
),
|
||||
metadict=_metadata.__dict__,
|
||||
)
|
||||
|
||||
globals()["__dir__"] = dir_func
|
||||
globals()["__getattr__"] = getattr_func
|
||||
|
||||
del sys.modules["Foundation._metadata"]
|
||||
|
||||
objc.addConvenienceForClass(
|
||||
"NSAttributedString", (("__len__", lambda self: self.length()),)
|
||||
)
|
||||
|
||||
objc.addConvenienceForBasicMapping("NSMergeConflict", True)
|
||||
objc.addConvenienceForBasicMapping("NSUbiquitousKeyValueStore", False)
|
||||
objc.addConvenienceForBasicMapping("NSUserDefaults", False)
|
||||
|
||||
NSNull = objc.lookUpClass("NSNull")
|
||||
|
||||
def nscache_getitem(self, key):
|
||||
value = self.objectForKey_(key)
|
||||
if value is None:
|
||||
raise KeyError(key)
|
||||
|
||||
elif value is NSNull.null():
|
||||
return None
|
||||
|
||||
else:
|
||||
return value
|
||||
|
||||
def nscache_get(self, key, default=None):
|
||||
value = self.objectForKey_(key)
|
||||
if value is None:
|
||||
return default
|
||||
elif value is NSNull.null():
|
||||
return None
|
||||
return value
|
||||
|
||||
def nscache_setitem(self, key, value):
|
||||
if value is None:
|
||||
value = NSNull.null()
|
||||
self.setObject_forKey_(value, key)
|
||||
|
||||
objc.addConvenienceForClass(
|
||||
"NSCache",
|
||||
(
|
||||
("__getitem__", nscache_getitem),
|
||||
("get", nscache_get),
|
||||
("__setitem__", nscache_setitem),
|
||||
("__delitem__", lambda self, key: self.removeObjectForKey_(key)),
|
||||
("clear", lambda self: self.removeAllObjects()),
|
||||
),
|
||||
)
|
||||
|
||||
def hash_add(self, value):
|
||||
if value is None:
|
||||
value = NSNull.null()
|
||||
self.addObject_(value)
|
||||
|
||||
def hash_contains(self, value):
|
||||
if value is None:
|
||||
value = NSNull.null()
|
||||
return self.containsObject_(value)
|
||||
|
||||
def hash_remove(self, value):
|
||||
if value is None:
|
||||
value = NSNull.null()
|
||||
self.removeObject_(value)
|
||||
|
||||
def hash_pop(self):
|
||||
value = self.anyObject()
|
||||
self.removeObject_(value)
|
||||
if value is NSNull.null():
|
||||
return None
|
||||
else:
|
||||
return value
|
||||
|
||||
objc.addConvenienceForClass(
|
||||
"NSHashTable",
|
||||
(
|
||||
("__len__", lambda self: self.count()),
|
||||
("clear", lambda self: self.removeAllObjects()),
|
||||
("__iter__", lambda self: iter(self.objectEnumerator())),
|
||||
("add", hash_add),
|
||||
("remove", hash_remove),
|
||||
("__contains__", hash_contains),
|
||||
("pop", hash_pop),
|
||||
),
|
||||
)
|
||||
|
||||
objc.addConvenienceForClass(
|
||||
"NSIndexPath", (("__len__", lambda self: self.count()),)
|
||||
)
|
||||
|
||||
if sys.maxsize > 2**32:
|
||||
NSNotFound = 0x7FFFFFFFFFFFFFFF
|
||||
else:
|
||||
NSNotFound = 0x7FFFFFFF
|
||||
|
||||
def indexset_iter(self):
|
||||
value = self.firstIndex()
|
||||
while value != NSNotFound:
|
||||
yield value
|
||||
value = self.indexGreaterThanIndex_(value)
|
||||
|
||||
def indexset_reversed(self):
|
||||
value = self.lastIndex()
|
||||
while value != NSNotFound:
|
||||
yield value
|
||||
value = self.indexLessThanIndex_(value)
|
||||
|
||||
NSIndexSet = objc.lookUpClass("NSIndexSet")
|
||||
|
||||
def indexset_eq(self, other):
|
||||
if not isinstance(other, NSIndexSet):
|
||||
return False
|
||||
|
||||
return self.isEqualToIndexSet_(other)
|
||||
|
||||
def indexset_ne(self, other):
|
||||
if not isinstance(other, NSIndexSet):
|
||||
return True
|
||||
|
||||
return not self.isEqualToIndexSet_(other)
|
||||
|
||||
def indexset_contains(self, value):
|
||||
try:
|
||||
return self.containsIndex_(value)
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
objc.addConvenienceForClass(
|
||||
"NSIndexSet",
|
||||
(
|
||||
("__len__", lambda self: self.count()),
|
||||
("__iter__", indexset_iter),
|
||||
("__reversed__", indexset_reversed),
|
||||
("__eq__", indexset_eq),
|
||||
("__ne__", indexset_ne),
|
||||
("__contains__", indexset_contains),
|
||||
),
|
||||
)
|
||||
|
||||
# Add 'update', '-=', '+='
|
||||
objc.addConvenienceForClass(
|
||||
"NSMutableIndexSet",
|
||||
(
|
||||
("clear", lambda self: self.removeAllIndexes()),
|
||||
("add", lambda self, value: self.addIndex_(value)),
|
||||
("remove", lambda self, value: self.removeIndex_(value)),
|
||||
),
|
||||
)
|
||||
|
||||
objc.addConvenienceForClass(
|
||||
"NSLocale", (("__getitem__", lambda self, key: self.objectForKey_(key)),)
|
||||
)
|
||||
|
||||
|
||||
globals().pop("_setup")()
|
||||
|
||||
from objc import NSDecimal, YES, NO # isort:skip # noqa: E402, F401
|
||||
|
||||
import Foundation._context # isort:skip # noqa: E402
|
||||
import Foundation._functiondefines # isort:skip # noqa: E402
|
||||
import Foundation._nsindexset # isort:skip # noqa: E402
|
||||
import Foundation._nsobject # isort:skip # noqa: E402, F401
|
||||
import Foundation._nsurl # isort:skip # noqa: E402, F401
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -0,0 +1,26 @@
|
|||
import Foundation
|
||||
|
||||
|
||||
class NSDisabledAutomaticTermination:
|
||||
def __init__(self, reason):
|
||||
self._reason = reason
|
||||
self._info = Foundation.NSProcessInfo.processInfo()
|
||||
|
||||
def __enter__(self):
|
||||
self._info.disableAutomaticTermination_(self._reason)
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self._info.enableAutomaticTermination_(self._reason)
|
||||
return False
|
||||
|
||||
|
||||
class NSDisabledSuddenTermination:
|
||||
def __init__(self):
|
||||
self._info = Foundation.NSProcessInfo.processInfo()
|
||||
|
||||
def __enter__(self):
|
||||
self._info.disableSuddenTermination()
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self._info.enableSuddenTermination()
|
||||
return False
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
"""
|
||||
Port of "function defines".
|
||||
"""
|
||||
import Foundation as _Foundation
|
||||
|
||||
|
||||
def NSLocalizedString(key, comment):
|
||||
return _Foundation.NSBundle.mainBundle().localizedStringForKey_value_table_(
|
||||
key, "", None
|
||||
)
|
||||
|
||||
|
||||
def NSLocalizedStringFromTable(key, tbl, comment):
|
||||
return _Foundation.NSBundle.mainBundle().localizedStringForKey_value_table_(
|
||||
key, "", tbl
|
||||
)
|
||||
|
||||
|
||||
def NSLocalizedStringFromTableInBundle(key, tbl, bundle, comment):
|
||||
return bundle.localizedStringForKey_value_table_(key, "", tbl)
|
||||
|
||||
|
||||
def NSLocalizedStringWithDefaultValue(key, tbl, bundle, val, comment):
|
||||
return bundle.localizedStringForKey_value_table_(key, val, tbl)
|
||||
|
||||
|
||||
def NSLocalizedAttributedString(key, comment):
|
||||
return (
|
||||
_Foundation.NSBundle.mainBundle().localizedAttributedStringForKey_value_table_(
|
||||
key, "", None
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def NSLocalizedAttributedStringFromTable(key, tbl, comment):
|
||||
return _Foundation.NSBundle.mainBundle.localizedAttributedStringForKey_value_table_(
|
||||
key, "", tbl
|
||||
)
|
||||
|
||||
|
||||
def NSLocalizedAttributedStringFromTableInBundle(key, tbl, bundle, comment):
|
||||
return bundle.localizedAttributedStringForKey_value_table_(key, "", tbl)
|
||||
|
||||
|
||||
def NSLocalizedAttributedStringWithDefaultValue(key, tbl, bundle, val, comment):
|
||||
return bundle.localizedAttributedStringForKey_value_table_(key, val, tbl)
|
||||
|
||||
|
||||
def MIN(a, b):
|
||||
if a < b:
|
||||
return a
|
||||
else:
|
||||
return b
|
||||
|
||||
|
||||
def MAX(a, b):
|
||||
if a < b:
|
||||
return b
|
||||
else:
|
||||
return a
|
||||
|
||||
|
||||
ABS = abs
|
||||
Binary file not shown.
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,21 @@
|
|||
import objc
|
||||
|
||||
|
||||
def __len__(self):
|
||||
return self.length()
|
||||
|
||||
|
||||
def __getitem__(self, idx):
|
||||
if isinstance(idx, slice):
|
||||
raise ValueError(idx)
|
||||
return self.indexAtPosition_(idx)
|
||||
|
||||
|
||||
def __add__(self, value):
|
||||
return self.indexPathByAddingIndex_(value)
|
||||
|
||||
|
||||
objc.addConvenienceForClass(
|
||||
"NSIndexPath",
|
||||
(("__len__", __len__), ("__getitem__", __getitem__), ("__add__", __add__)),
|
||||
)
|
||||
|
|
@ -0,0 +1,235 @@
|
|||
"""
|
||||
Define a category on NSObject with some useful methods.
|
||||
"""
|
||||
import sys
|
||||
|
||||
import objc
|
||||
|
||||
if sys.version_info[0] == 2:
|
||||
|
||||
def _str(v):
|
||||
return v
|
||||
|
||||
exec(
|
||||
"""\
|
||||
def _raise(exc_type, exc_value, exc_trace):
|
||||
raise exc_type, exc_value, exc_trace
|
||||
"""
|
||||
)
|
||||
else:
|
||||
|
||||
def _str(v):
|
||||
if isinstance(v, str):
|
||||
return v
|
||||
return v.decode("ascii")
|
||||
|
||||
def _raise(exc_type, exc_value, exc_trace):
|
||||
raise exc_type(exc_value).with_traceback(exc_trace)
|
||||
|
||||
|
||||
NSObject = objc.lookUpClass("NSObject")
|
||||
|
||||
|
||||
class NSObject(objc.Category(NSObject)):
|
||||
@objc.namedSelector(b"_pyobjc_performOnThread:")
|
||||
def _pyobjc_performOnThread_(self, callinfo):
|
||||
try:
|
||||
sel, arg = callinfo
|
||||
m = getattr(self, _str(sel))
|
||||
m(arg)
|
||||
except: # noqa: E722, B001
|
||||
import traceback
|
||||
|
||||
traceback.print_exc(file=sys.stderr)
|
||||
|
||||
@objc.namedSelector(b"_pyobjc_performOnThreadWithResult:")
|
||||
def _pyobjc_performOnThreadWithResult_(self, callinfo):
|
||||
try:
|
||||
sel, arg, result = callinfo
|
||||
m = getattr(self, _str(sel))
|
||||
r = m(arg)
|
||||
result.append((True, r))
|
||||
except: # noqa: E722, B001
|
||||
result.append((False, sys.exc_info()))
|
||||
|
||||
if hasattr(NSObject, "performSelector_onThread_withObject_waitUntilDone_"):
|
||||
|
||||
@objc.namedSelector(
|
||||
b"pyobjc_performSelector:onThread:withObject:waitUntilDone:"
|
||||
)
|
||||
def pyobjc_performSelector_onThread_withObject_waitUntilDone_(
|
||||
self, aSelector, thread, arg, wait
|
||||
):
|
||||
"""
|
||||
A version of performSelector:onThread:withObject:waitUntilDone: that
|
||||
will log exceptions in the called method (instead of aborting the
|
||||
NSRunLoop on the other thread).
|
||||
"""
|
||||
self.performSelector_onThread_withObject_waitUntilDone_(
|
||||
b"_pyobjc_performOnThread:", thread, (aSelector, arg), wait
|
||||
)
|
||||
|
||||
@objc.namedSelector(
|
||||
b"pyobjc_performSelector:onThread:withObject:waitUntilDone:modes:"
|
||||
)
|
||||
def pyobjc_performSelector_onThread_withObject_waitUntilDone_modes_(
|
||||
self, aSelector, thread, arg, wait, modes
|
||||
):
|
||||
"""
|
||||
A version of performSelector:onThread:withObject:waitUntilDone:modes:
|
||||
that will log exceptions in the called method (instead of aborting the
|
||||
NSRunLoop on the other thread).
|
||||
"""
|
||||
self.performSelector_onThread_withObject_waitUntilDone_modes_(
|
||||
b"_pyobjc_performOnThread:", thread, (aSelector, arg), wait, modes
|
||||
)
|
||||
|
||||
@objc.namedSelector(b"pyobjc_performSelector:withObject:afterDelay:")
|
||||
def pyobjc_performSelector_withObject_afterDelay_(self, aSelector, arg, delay):
|
||||
"""
|
||||
A version of performSelector:withObject:afterDelay:
|
||||
that will log exceptions in the called method (instead of aborting the
|
||||
NSRunLoop).
|
||||
"""
|
||||
self.performSelector_withObject_afterDelay_(
|
||||
b"_pyobjc_performOnThread:", (aSelector, arg), delay
|
||||
)
|
||||
|
||||
@objc.namedSelector(b"pyobjc_performSelector:withObject:afterDelay:inModes:")
|
||||
def pyobjc_performSelector_withObject_afterDelay_inModes_(
|
||||
self, aSelector, arg, delay, modes
|
||||
):
|
||||
"""
|
||||
A version of performSelector:withObject:afterDelay:inModes:
|
||||
that will log exceptions in the called method (instead of aborting the
|
||||
NSRunLoop).
|
||||
"""
|
||||
self.performSelector_withObject_afterDelay_inModes_(
|
||||
b"_pyobjc_performOnThread:", (aSelector, arg), delay, modes
|
||||
)
|
||||
|
||||
if hasattr(NSObject, "performSelectorInBackground_withObject_"):
|
||||
|
||||
@objc.namedSelector(b"pyobjc_performSelectorInBackground:withObject:")
|
||||
def pyobjc_performSelectorInBackground_withObject_(self, aSelector, arg):
|
||||
"""
|
||||
A version of performSelectorInBackground:withObject:
|
||||
that will log exceptions in the called method (instead of aborting the
|
||||
NSRunLoop).
|
||||
"""
|
||||
self.performSelectorInBackground_withObject_(
|
||||
b"_pyobjc_performOnThread:", (aSelector, arg)
|
||||
)
|
||||
|
||||
@objc.namedSelector(b"pyobjc_performSelectorOnMainThread:withObject:waitUntilDone:")
|
||||
def pyobjc_performSelectorOnMainThread_withObject_waitUntilDone_(
|
||||
self, aSelector, arg, wait
|
||||
):
|
||||
"""
|
||||
A version of performSelectorOnMainThread:withObject:waitUntilDone:
|
||||
that will log exceptions in the called method (instead of aborting the
|
||||
NSRunLoop in the main thread).
|
||||
"""
|
||||
self.performSelectorOnMainThread_withObject_waitUntilDone_(
|
||||
b"_pyobjc_performOnThread:", (aSelector, arg), wait
|
||||
)
|
||||
|
||||
@objc.namedSelector(
|
||||
b"pyobjc_performSelectorOnMainThread:withObject:waitUntilDone:modes:"
|
||||
)
|
||||
def pyobjc_performSelectorOnMainThread_withObject_waitUntilDone_modes_(
|
||||
self, aSelector, arg, wait, modes
|
||||
):
|
||||
"""
|
||||
A version of performSelectorOnMainThread:withObject:waitUntilDone:modes:
|
||||
that will log exceptions in the called method (instead of aborting the
|
||||
NSRunLoop in the main thread).
|
||||
"""
|
||||
self.performSelectorOnMainThread_withObject_waitUntilDone_modes_(
|
||||
b"_pyobjc_performOnThread:", (aSelector, arg), wait, modes
|
||||
)
|
||||
|
||||
# And some a some versions that return results
|
||||
|
||||
@objc.namedSelector(b"pyobjc_performSelectorOnMainThread:withObject:modes:")
|
||||
def pyobjc_performSelectorOnMainThread_withObject_modes_(
|
||||
self, aSelector, arg, modes
|
||||
):
|
||||
"""
|
||||
Simular to performSelectorOnMainThread:withObject:waitUntilDone:modes:,
|
||||
but:
|
||||
|
||||
- always waits until done
|
||||
- returns the return value of the called method
|
||||
- if the called method raises an exception, this will raise the same
|
||||
exception
|
||||
"""
|
||||
result = []
|
||||
self.performSelectorOnMainThread_withObject_waitUntilDone_modes_(
|
||||
b"_pyobjc_performOnThreadWithResult:", (aSelector, arg, result), True, modes
|
||||
)
|
||||
isOK, result = result[0]
|
||||
|
||||
if isOK:
|
||||
return result
|
||||
else:
|
||||
exc_type, exc_value, exc_trace = result
|
||||
_raise(exc_type, exc_value, exc_trace)
|
||||
|
||||
@objc.namedSelector(b"pyobjc_performSelectorOnMainThread:withObject:")
|
||||
def pyobjc_performSelectorOnMainThread_withObject_(self, aSelector, arg):
|
||||
result = []
|
||||
self.performSelectorOnMainThread_withObject_waitUntilDone_(
|
||||
b"_pyobjc_performOnThreadWithResult:", (aSelector, arg, result), True
|
||||
)
|
||||
isOK, result = result[0]
|
||||
|
||||
if isOK:
|
||||
return result
|
||||
else:
|
||||
exc_type, exc_value, exc_trace = result
|
||||
_raise(exc_type, exc_value, exc_trace)
|
||||
|
||||
if hasattr(NSObject, "performSelector_onThread_withObject_waitUntilDone_"):
|
||||
# These methods require Leopard, don't define them if the
|
||||
# platform functionality isn't present.
|
||||
|
||||
@objc.namedSelector(b"pyobjc_performSelector:onThread:withObject:modes:")
|
||||
def pyobjc_performSelector_onThread_withObject_modes_(
|
||||
self, aSelector, thread, arg, modes
|
||||
):
|
||||
result = []
|
||||
self.performSelector_onThread_withObject_waitUntilDone_modes_(
|
||||
b"_pyobjc_performOnThreadWithResult:",
|
||||
thread,
|
||||
(aSelector, arg, result),
|
||||
True,
|
||||
modes,
|
||||
)
|
||||
isOK, result = result[0]
|
||||
|
||||
if isOK:
|
||||
return result
|
||||
else:
|
||||
exc_type, exc_value, exc_trace = result
|
||||
_raise(exc_type, exc_value, exc_trace)
|
||||
|
||||
@objc.namedSelector(b"pyobjc_performSelector:onThread:withObject:")
|
||||
def pyobjc_performSelector_onThread_withObject_(self, aSelector, thread, arg):
|
||||
result = []
|
||||
self.performSelector_onThread_withObject_waitUntilDone_(
|
||||
b"_pyobjc_performOnThreadWithResult:",
|
||||
thread,
|
||||
(aSelector, arg, result),
|
||||
True,
|
||||
)
|
||||
isOK, result = result[0]
|
||||
|
||||
if isOK:
|
||||
return result
|
||||
else:
|
||||
exc_type, exc_value, exc_trace = result
|
||||
_raise(exc_type, exc_value, exc_trace)
|
||||
|
||||
|
||||
del NSObject
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
"""
|
||||
Helpers for NSURL
|
||||
"""
|
||||
import sys
|
||||
import objc
|
||||
|
||||
|
||||
def __fspath__(self):
|
||||
if self.scheme() == "file":
|
||||
# self.fileSystemRepresentation returns a byte string,
|
||||
# whereas most user code expects regular strings. Decode
|
||||
# in the same way as extension functions in the ``os`` module.
|
||||
return self.fileSystemRepresentation().decode(
|
||||
sys.getfilesystemencoding(), sys.getfilesystemencodeerrors()
|
||||
)
|
||||
|
||||
raise TypeError(f"NSURL with scheme {self.scheme()!r} instead of 'file'")
|
||||
|
||||
|
||||
objc.addConvenienceForClass(
|
||||
"NSURL",
|
||||
(("__fspath__", __fspath__),),
|
||||
)
|
||||
|
|
@ -0,0 +1 @@
|
|||
pip
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
Copyright 2010 Pallets
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
Metadata-Version: 2.1
|
||||
Name: MarkupSafe
|
||||
Version: 3.0.2
|
||||
Summary: Safely add untrusted strings to HTML/XML markup.
|
||||
Maintainer-email: Pallets <contact@palletsprojects.com>
|
||||
License: Copyright 2010 Pallets
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
|
||||
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
Project-URL: Donate, https://palletsprojects.com/donate
|
||||
Project-URL: Documentation, https://markupsafe.palletsprojects.com/
|
||||
Project-URL: Changes, https://markupsafe.palletsprojects.com/changes/
|
||||
Project-URL: Source, https://github.com/pallets/markupsafe/
|
||||
Project-URL: Chat, https://discord.gg/pallets
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Environment :: Web Environment
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: License :: OSI Approved :: BSD License
|
||||
Classifier: Operating System :: OS Independent
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
|
||||
Classifier: Topic :: Text Processing :: Markup :: HTML
|
||||
Classifier: Typing :: Typed
|
||||
Requires-Python: >=3.9
|
||||
Description-Content-Type: text/markdown
|
||||
License-File: LICENSE.txt
|
||||
|
||||
# MarkupSafe
|
||||
|
||||
MarkupSafe implements a text object that escapes characters so it is
|
||||
safe to use in HTML and XML. Characters that have special meanings are
|
||||
replaced so that they display as the actual characters. This mitigates
|
||||
injection attacks, meaning untrusted user input can safely be displayed
|
||||
on a page.
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
```pycon
|
||||
>>> from markupsafe import Markup, escape
|
||||
|
||||
>>> # escape replaces special characters and wraps in Markup
|
||||
>>> escape("<script>alert(document.cookie);</script>")
|
||||
Markup('<script>alert(document.cookie);</script>')
|
||||
|
||||
>>> # wrap in Markup to mark text "safe" and prevent escaping
|
||||
>>> Markup("<strong>Hello</strong>")
|
||||
Markup('<strong>hello</strong>')
|
||||
|
||||
>>> escape(Markup("<strong>Hello</strong>"))
|
||||
Markup('<strong>hello</strong>')
|
||||
|
||||
>>> # Markup is a str subclass
|
||||
>>> # methods and operators escape their arguments
|
||||
>>> template = Markup("Hello <em>{name}</em>")
|
||||
>>> template.format(name='"World"')
|
||||
Markup('Hello <em>"World"</em>')
|
||||
```
|
||||
|
||||
## Donate
|
||||
|
||||
The Pallets organization develops and supports MarkupSafe and other
|
||||
popular packages. In order to grow the community of contributors and
|
||||
users, and allow the maintainers to devote more time to the projects,
|
||||
[please donate today][].
|
||||
|
||||
[please donate today]: https://palletsprojects.com/donate
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
MarkupSafe-3.0.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||
MarkupSafe-3.0.2.dist-info/LICENSE.txt,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475
|
||||
MarkupSafe-3.0.2.dist-info/METADATA,sha256=aAwbZhSmXdfFuMM-rEHpeiHRkBOGESyVLJIuwzHP-nw,3975
|
||||
MarkupSafe-3.0.2.dist-info/RECORD,,
|
||||
MarkupSafe-3.0.2.dist-info/WHEEL,sha256=lXrF9eVJm7UF3ZOBaBu2Y-RekBGubHbC1Bvbd4BEjAQ,109
|
||||
MarkupSafe-3.0.2.dist-info/top_level.txt,sha256=qy0Plje5IJuvsCBjejJyhDCjEAdcDLK_2agVcex8Z6U,11
|
||||
markupsafe/__init__.py,sha256=sr-U6_27DfaSrj5jnHYxWN-pvhM27sjlDplMDPZKm7k,13214
|
||||
markupsafe/__pycache__/__init__.cpython-310.pyc,,
|
||||
markupsafe/__pycache__/_native.cpython-310.pyc,,
|
||||
markupsafe/_native.py,sha256=hSLs8Jmz5aqayuengJJ3kdT5PwNpBWpKrmQSdipndC8,210
|
||||
markupsafe/_speedups.c,sha256=O7XulmTo-epI6n2FtMVOrJXl8EAaIwD2iNYmBI5SEoQ,4149
|
||||
markupsafe/_speedups.cpython-310-darwin.so,sha256=3f9Fj2FHr4Ue7g2ODjkMpKlhPZCbjE0h7ZPvY4L8aUs,50688
|
||||
markupsafe/_speedups.pyi,sha256=ENd1bYe7gbBUf2ywyYWOGUpnXOHNJ-cgTNqetlW8h5k,41
|
||||
markupsafe/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
Wheel-Version: 1.0
|
||||
Generator: setuptools (75.2.0)
|
||||
Root-Is-Purelib: false
|
||||
Tag: cp310-cp310-macosx_11_0_arm64
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
markupsafe
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
Metadata-Version: 2.1
|
||||
Name: MouseInfo
|
||||
Version: 0.1.3
|
||||
Summary: An application to display XY position and RGB color information for the pixel currently under the mouse. Works on Python 2 and 3.
|
||||
Home-page: https://github.com/asweigart/mouseinfo
|
||||
Author: Al Sweigart
|
||||
Author-email: al@inventwithpython.com
|
||||
License: GPLv3+
|
||||
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 2
|
||||
Classifier: Programming Language :: Python :: 2.7
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.2
|
||||
Classifier: Programming Language :: Python :: 3.3
|
||||
Classifier: Programming Language :: Python :: 3.4
|
||||
Classifier: Programming Language :: Python :: 3.5
|
||||
Classifier: Programming Language :: Python :: 3.6
|
||||
Classifier: Programming Language :: Python :: 3.7
|
||||
Description-Content-Type: text/markdown
|
||||
|
||||
MouseInfo
|
||||
======
|
||||
|
||||
An application to display XY position and RGB color information for the pixel currently under the mouse. Works on Python 2 and 3. This is useful for GUI automation planning.
|
||||
|
||||
The full documentation is at https://mouseinfo.readthedocs.io/en/latest/
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
To install with pip, run:
|
||||
|
||||
pip install mouseinfo
|
||||
|
||||
Quickstart Guide
|
||||
----------------
|
||||
|
||||
To run this application, enter the following into the terminal:
|
||||
|
||||
python3 -m mouseinfo
|
||||
|
||||
Or for Python 2, run:
|
||||
|
||||
python -m mouseinfo
|
||||
|
||||
Alternatively, to run it from the interactive shell or a Python program:
|
||||
|
||||
>>> import mouseinfo
|
||||
>>> mouseinfo.mouseInfo()
|
||||
|
||||
The Mouse Info application displays the current XY coordinates of the mouse cursor, as well as the RGB color information of the pixel directly under the cursor. This can be useful for planning out GUI automation tests where the mouse is controlled by a script (such as a Python script with PyAutoGUI) to click on the screen at specific coordinates.
|
||||
|
||||
The "Copy" buttons will copy this mouse information to the clipboard, while the "Log" buttons will add this mouse information to the text field in the application. The RGB color information is given as a comman-delimited, three-integer red, green, and blue values as decimals from 0 to 255. The hex values of the RGB value is also given.
|
||||
|
||||
For practical use, you should set the keyboard focus on these buttons by tabbing over them. This leaves you free to move the mouse into position and then press space or Enter to log the current mouse coordinates/RGB value.
|
||||
|
||||
The contents of the log text field can be saved by clicking "Save Log". This will automatically overwrite any file with the provided name. A screenshot can also be saved by clicking "Save Screenshot"
|
||||
|
||||
Contribute
|
||||
----------
|
||||
|
||||
If you'd like to contribute to MouseInfo, check out https://github.com/asweigart/mouseinfo
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
MANIFEST.in
|
||||
README.md
|
||||
setup.cfg
|
||||
setup.py
|
||||
src/MouseInfo.egg-info/PKG-INFO
|
||||
src/MouseInfo.egg-info/SOURCES.txt
|
||||
src/MouseInfo.egg-info/dependency_links.txt
|
||||
src/MouseInfo.egg-info/requires.txt
|
||||
src/MouseInfo.egg-info/top_level.txt
|
||||
src/mouseinfo/__init__.py
|
||||
src/mouseinfo/__main__.py
|
||||
|
|
@ -0,0 +1 @@
|
|||
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
../mouseinfo/__init__.py
|
||||
../mouseinfo/__main__.py
|
||||
../mouseinfo/__pycache__/__init__.cpython-310.pyc
|
||||
../mouseinfo/__pycache__/__main__.cpython-310.pyc
|
||||
PKG-INFO
|
||||
SOURCES.txt
|
||||
dependency_links.txt
|
||||
requires.txt
|
||||
top_level.txt
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
pyperclip
|
||||
|
||||
[:platform_system == "Darwin"]
|
||||
rubicon-objc
|
||||
|
||||
[:platform_system == "Linux" and python_version < "3.0"]
|
||||
Xlib
|
||||
|
||||
[:platform_system == "Linux" and python_version >= "3.0"]
|
||||
python3-Xlib
|
||||
|
||||
[:python_version == "2.7"]
|
||||
Pillow>=2.0.0
|
||||
|
||||
[:python_version == "3.2"]
|
||||
Pillow<=3.4.2,>=2.0.0
|
||||
|
||||
[:python_version == "3.3"]
|
||||
Pillow<=4.3.0,>=2.0.0
|
||||
|
||||
[:python_version == "3.4"]
|
||||
Pillow<=5.4.1,>=2.5.0
|
||||
|
||||
[:python_version == "3.5"]
|
||||
Pillow>=3.2.0
|
||||
|
||||
[:python_version == "3.6"]
|
||||
Pillow>=4.0.0
|
||||
|
||||
[:python_version == "3.7"]
|
||||
Pillow>=5.2.0
|
||||
|
|
@ -0,0 +1 @@
|
|||
mouseinfo
|
||||
Binary file not shown.
BIN
write-message/venv/lib/python3.10/site-packages/PIL/.dylibs/libavif.16.3.0.dylib
Executable file
BIN
write-message/venv/lib/python3.10/site-packages/PIL/.dylibs/libavif.16.3.0.dylib
Executable file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
write-message/venv/lib/python3.10/site-packages/PIL/.dylibs/libjpeg.62.4.0.dylib
Executable file
BIN
write-message/venv/lib/python3.10/site-packages/PIL/.dylibs/libjpeg.62.4.0.dylib
Executable file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue