From b1720b8a2104b78f8f0e4b59e5c17c47e2f32ec8 Mon Sep 17 00:00:00 2001 From: albnnc Date: Thu, 9 Jul 2026 00:33:51 +0300 Subject: [PATCH] w --- .editorconfig | 8 ++ .gitattributes | 1 + .gitignore | 12 +++ dprint.json | 18 ++++ main.ts | 148 ++++++++++++++++++++++++++++ package-lock.json | 238 ++++++++++++++++++++++++++++++++++++++++++++++ package.json | 13 +++ tsconfig.json | 42 ++++++++ 8 files changed, 480 insertions(+) create mode 100644 .editorconfig create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 dprint.json create mode 100644 main.ts create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 tsconfig.json diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..b261f16 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,8 @@ +root = true + +[*] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true \ No newline at end of file diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..e51fdcc --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +/.pnp.* binary linguist-generated \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c46c3e7 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +.cache +.data +.DS_Store +.env +.git +.idea +.pnp.* +.target +.tmp +.vscode +.yarn +node_modules \ No newline at end of file diff --git a/dprint.json b/dprint.json new file mode 100644 index 0000000..b9bf997 --- /dev/null +++ b/dprint.json @@ -0,0 +1,18 @@ +{ + "lineWidth": 80, + "indentWidth": 2, + "typescript": { + "module.sortImportDeclarations": "caseInsensitive", + "module.sortExportDeclarations": "caseInsensitive" + }, + "excludes": [ + "**/.git", + "**/.target" + ], + "plugins": [ + "https://plugins.dprint.dev/typescript-0.91.1.wasm", + "https://plugins.dprint.dev/json-0.17.4.wasm", + "https://plugins.dprint.dev/markdown-0.15.3.wasm", + "https://plugins.dprint.dev/dockerfile-0.3.0.wasm" + ] +} diff --git a/main.ts b/main.ts new file mode 100644 index 0000000..e4704d1 --- /dev/null +++ b/main.ts @@ -0,0 +1,148 @@ +import { XMLParser } from "fast-xml-parser"; +import { promisify } from "node:util"; +import { env } from "node:process"; +import { createRequire } from "node:module"; +import https from "node:https"; + +const ntlm: any = createRequire(import.meta.url)("httpntlm"); +const postAsync = promisify(ntlm.post.bind(ntlm)); + +const SSL_OP_LEGACY_SERVER_CONNECT = 0x00000004; + +const AGENT = new https.Agent({ + keepAlive: true, + rejectUnauthorized: false, + secureOptions: SSL_OP_LEGACY_SERVER_CONNECT, +}); + +const EWS_URL = "https://mail.b1.ru/EWS/Exchange.asmx"; + +const PARSER = new XMLParser({ + ignoreAttributes: false, + attributeNamePrefix: "@_", + removeNSPrefix: true, + textNodeName: "#text", +}); + +function buildSoapEnvelope(body: string): string { + return ` + + +${body} + +`; +} + +async function ewsSoap(options: { body: string; soapAction: string }): Promise { + const res = await postAsync({ + url: EWS_URL, + username: env.EWS_USERNAME ?? "polina.litvinova", + domain: "corp", + password: env.EWS_PASSWORD, + agent: AGENT, + headers: { + "Content-Type": "text/xml; charset=utf-8", + SOAPAction: options.soapAction, + }, + body: options.body, + }); + + if (typeof res.body !== "string") { + throw new Error(`NTLM request failed, status=${res.statusCode}`); + } + return PARSER.parse(res.body); +} + +async function getFolder() { + const soap = buildSoapEnvelope(`\ + + + IdOnly + + + + + + + + + + `); + + const data = await ewsSoap({ + body: soap, + soapAction: "http://schemas.microsoft.com/exchange/services/2006/messages/GetFolder", + }); + + const rm = data.Envelope?.Body?.GetFolderResponse?.ResponseMessages; + const msg = rm?.GetFolderResponseMessage; + const folder = msg?.Folders?.Folder; + + console.log(`DisplayName: ${folder?.DisplayName}`); + console.log(`FolderId: ${folder?.FolderId?.["@_Id"]}`); + console.log(`Total items: ${folder?.TotalCount}`); + console.log(`Unread: ${folder?.UnreadCount}`); +} + +async function getLastMessages() { + const soap = buildSoapEnvelope(`\ + + + AllProperties + + + + + + + + + + + `); + + const data = await ewsSoap({ + body: soap, + soapAction: "http://schemas.microsoft.com/exchange/services/2006/messages/FindItem", + }); + + const rm = data.Envelope?.Body?.FindItemResponse?.ResponseMessages; + const msg = rm?.FindItemResponseMessage; + const rootFolder = msg?.RootFolder; + const items = rootFolder?.Items; + const messagesList: any[] = items?.Message ? (Array.isArray(items.Message) ? items.Message : [items.Message]) : []; + + if (!messagesList.length) { + console.log("\n--- No messages found ---"); + return; + } + + console.log(`\n--- ${messagesList.length} message(s) ---`); + for (const m of messagesList) { + const mbox = m.From?.Mailbox; + const author = mbox ? `${mbox.Name ?? "(no name)"} <${mbox.EmailAddress ?? "(no email)"}>` : "(unknown)"; + console.log(`\nSubject: ${m.Subject ?? "(no subject)"}`); + console.log(`From: ${author}`); + console.log(`Date: ${m.DateTimeReceived ?? "(unknown)"}`); + console.log(`Preview: ${(m.Preview ?? "(no preview)").slice(0, 200)}`); + } +} + +async function main() { + if (!env.EWS_PASSWORD) { + console.error("Set EWS_PASSWORD environment variable"); + process.exit(1); + } + + try { + await getFolder(); + await getLastMessages(); + } catch (err) { + console.error("Error:", err); + process.exit(1); + } +} + +main(); \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..2bf61c1 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,238 @@ +{ + "name": "exchange-mcp", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "exchange-mcp", + "version": "0.0.0", + "dependencies": { + "fast-xml-parser": "^5.2.0", + "httpntlm": "^1.8.13" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "typescript": "^5.9.3" + } + }, + "node_modules/@nodable/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-9uGyhaQavEUMC8AIddIjau4NsnsXhou+j5sBAGojCM1oxmQpVKTWR/9JxABD6UAv12vpIms55fPZKFQEhG6uBg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/nodable" + } + ], + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.20.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.1.tgz", + "integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/anynum": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/anynum/-/anynum-1.0.1.tgz", + "integrity": "sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/des.js": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.1.0.tgz", + "integrity": "sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "node_modules/fast-xml-builder": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.2.1.tgz", + "integrity": "sha512-tPb5TTWfgfVx5BNSi2xV0eLr89POeXXn0dXIsCJ9m1narrWxeIyx6je9d7Rce/3NyXLbvuQmLkxq+RuxMWejvw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "path-expression-matcher": "^1.5.0", + "xml-naming": "^0.1.0" + } + }, + "node_modules/fast-xml-parser": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.9.3.tgz", + "integrity": "sha512-brCNCeScma/kqa54J4PIDriSSSLssRkuYaUCpvHJulGc3HGI/xxKUCTDcYkAdqJsyb//ydpbxecjC3hB9+tb/g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "@nodable/entities": "^2.2.0", + "fast-xml-builder": "^1.2.0", + "is-unsafe": "^1.0.1", + "path-expression-matcher": "^1.5.0", + "strnum": "^2.4.1", + "xml-naming": "^0.1.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/httpntlm": { + "version": "1.8.13", + "resolved": "https://registry.npmjs.org/httpntlm/-/httpntlm-1.8.13.tgz", + "integrity": "sha512-2F2FDPiWT4rewPzNMg3uPhNkP3NExENlUGADRUDPQvuftuUTGW98nLZtGemCIW3G40VhWZYgkIDcQFAwZ3mf2Q==", + "funding": [ + { + "type": "paypal", + "url": "https://www.paypal.com/donate/?hosted_button_id=2CKNJLZJBW8ZC" + }, + { + "type": "buymeacoffee", + "url": "https://www.buymeacoffee.com/samdecrock" + } + ], + "dependencies": { + "des.js": "^1.0.1", + "httpreq": ">=0.4.22", + "js-md4": "^0.3.2", + "underscore": "~1.12.1" + }, + "engines": { + "node": ">=10.4.0" + } + }, + "node_modules/httpreq": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/httpreq/-/httpreq-1.1.1.tgz", + "integrity": "sha512-uhSZLPPD2VXXOSN8Cni3kIsoFHaU2pT/nySEU/fHr/ePbqHYr0jeiQRmUKLEirC09SFPsdMoA7LU7UXMd/w0Kw==", + "license": "MIT", + "engines": { + "node": ">= 6.15.1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/is-unsafe": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-unsafe/-/is-unsafe-1.0.1.tgz", + "integrity": "sha512-CLK2+VdgERgD96EYm5lUQssZYlRg2tkZnbsxZoacmSiRxiFJ4Nk4SzjCl+Ur+v3kXIY9dTIdb3IH22y1mZ56LA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/js-md4": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz", + "integrity": "sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==", + "license": "MIT" + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", + "license": "ISC" + }, + "node_modules/path-expression-matcher": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.6.2.tgz", + "integrity": "sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/strnum": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.4.1.tgz", + "integrity": "sha512-M9eUSMT2dCB2cTNPG7UYj6KuK7RJR2SN2+yCV/fTW3xzTCS6EaGZ5pSMgDIjB7r8zSfTGk+dvvn9rTjpVS9Mwg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "anynum": "^1.0.1" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/underscore": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.12.1.tgz", + "integrity": "sha512-hEQt0+ZLDVUMhebKxL4x1BTtDY7bavVofhZ9KZ4aI26X9SRaE+Y3m83XUL1UP2jn8ynjndwCCpEHdUG+9pP1Tw==", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/xml-naming": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.1.0.tgz", + "integrity": "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..1f68d12 --- /dev/null +++ b/package.json @@ -0,0 +1,13 @@ +{ + "name": "exchange-mcp", + "version": "0.0.0", + "type": "module", + "dependencies": { + "fast-xml-parser": "^5.2.0", + "httpntlm": "^1.8.13" + }, + "devDependencies": { + "@types/node": "^22.0.0", + "typescript": "^5.9.3" + } +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..424bf37 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,42 @@ +{ + "compilerOptions": { + "allowImportingTsExtensions": true, + "allowUnreachableCode": false, + "allowUnusedLabels": false, + "checkJs": false, + "erasableSyntaxOnly": true, + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "keyofStringsOnly": false, + "lib": [ + "esnext" + ], + "module": "nodenext", + "moduleResolution": "nodenext", + "noEmit": true, + "noErrorTruncation": false, + "noFallthroughCasesInSwitch": false, + "noImplicitAny": true, + "noImplicitOverride": true, + "noImplicitReturns": false, + "noImplicitThis": true, + "noStrictGenericChecks": false, + "noUncheckedIndexedAccess": false, + "noUnusedLocals": false, + "noUnusedParameters": false, + "skipLibCheck": true, + "strict": true, + "strictBindCallApply": true, + "strictFunctionTypes": true, + "strictNullChecks": true, + "strictPropertyInitialization": true, + "suppressExcessPropertyErrors": false, + "suppressImplicitAnyIndexErrors": false, + "target": "esnext", + "types": [ + "node" + ], + "useUnknownInCatchVariables": true, + "verbatimModuleSyntax": true + } +} \ No newline at end of file