update upload file

This commit is contained in:
Admin 2025-09-19 13:53:22 +07:00
parent 71756bbf4b
commit 5c36fa0d3d
5359 changed files with 837868 additions and 70 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

File diff suppressed because one or more lines are too long

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

BIN
write-message/.DS_Store vendored Normal file

Binary file not shown.

1
write-message/README.md Normal file
View File

@ -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.

101
write-message/main.py Normal file
View File

@ -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)}

View File

@ -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

View File

@ -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-----

28
write-message/ssl/key.pem Normal file
View File

@ -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-----

View File

@ -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"

View File

@ -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

View File

@ -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

View File

@ -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

8
write-message/venv/bin/distro Executable file
View File

@ -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())

8
write-message/venv/bin/dotenv Executable file
View File

@ -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())

View File

@ -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())

8
write-message/venv/bin/fastapi Executable file
View File

@ -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())

8
write-message/venv/bin/httpx Executable file
View File

@ -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())

View File

@ -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())

8
write-message/venv/bin/openai Executable file
View File

@ -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())

8
write-message/venv/bin/pip Executable file
View File

@ -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())

8
write-message/venv/bin/pip3 Executable file
View File

@ -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())

8
write-message/venv/bin/pip3.10 Executable file
View File

@ -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())

View File

@ -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())

View File

@ -0,0 +1 @@
python3

View File

@ -0,0 +1 @@
/Users/admin/.pyenv/versions/3.10.13/bin/python3

View File

@ -0,0 +1 @@
python3

8
write-message/venv/bin/tqdm Executable file
View File

@ -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())

8
write-message/venv/bin/typer Executable file
View File

@ -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())

8
write-message/venv/bin/uvicorn Executable file
View File

@ -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())

View File

@ -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())

View File

@ -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())

View File

@ -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}

File diff suppressed because one or more lines are too long

View File

@ -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

View File

@ -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")()

View File

@ -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")()

File diff suppressed because one or more lines are too long

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

File diff suppressed because one or more lines are too long

View File

@ -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__)),
)

View File

@ -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

View File

@ -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__),),
)

View File

@ -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.

View File

@ -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('&lt;script&gt;alert(document.cookie);&lt;/script&gt;')
>>> # 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>&#34;World&#34;</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

View File

@ -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

View File

@ -0,0 +1,5 @@
Wheel-Version: 1.0
Generator: setuptools (75.2.0)
Root-Is-Purelib: false
Tag: cp310-cp310-macosx_11_0_arm64

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

Some files were not shown because too many files have changed in this diff Show More