Skip to content

Commit 7de6c5e

Browse files
committed
initial code
1 parent 17939c0 commit 7de6c5e

40 files changed

+5462
-2
lines changed

.gitignore

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
yarn-debug.log*
6+
yarn-error.log*
7+
8+
# Runtime data
9+
pids
10+
*.pid
11+
*.seed
12+
*.pid.lock
13+
14+
# Directory for instrumented libs generated by jscoverage/JSCover
15+
lib-cov
16+
17+
# Coverage directory used by tools like istanbul
18+
coverage
19+
20+
# nyc test coverage
21+
.nyc_output
22+
23+
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24+
.grunt
25+
26+
# Bower dependency directory (https://bower.io/)
27+
bower_components
28+
29+
# node-waf configuration
30+
.lock-wscript
31+
32+
# Compiled binary addons (http://nodejs.org/api/addons.html)
33+
build/Release
34+
35+
# Dependency directories
36+
**/node_modules/
37+
jspm_packages/
38+
39+
# Typescript v1 declaration files
40+
typings/
41+
42+
# Optional npm cache directory
43+
.npm
44+
45+
# Optional eslint cache
46+
.eslintcache
47+
48+
# Optional REPL history
49+
.node_repl_history
50+
51+
# Output of 'npm pack'
52+
*.tgz
53+
54+
# Yarn Integrity file
55+
.yarn-integrity
56+
57+
# dotenv environment variables file
58+
.env
59+
60+
bin*/
61+
obj*/
62+
63+
build*/
64+
65+
node1*/
66+
smartcontracts*/
67+
68+
launch.json
69+
70+
apptokens.json

README.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,12 @@
1-
# ap-bot
2-
Sample Accounts Payable Bot
1+
# Accounts Payable Bot using Azure Bot Service
2+
3+
![Accounts Payable Bot using Azure Bot Service](https://raw.githubusercontent.com/jomit/ap-bot/master/architecture.png)
4+
5+
#### Prerequisites
6+
7+
TODO
8+
9+
#### Setup
10+
11+
TODO
12+

architecture.png

214 KB
Loading

console/app.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
require("dotenv").load();
2+
var messagebusjob = require("./messagebusjob");
3+
var fs = require("fs");
4+
5+
console.log("Starting Bot Processing Job...");
6+
messagebusjob.startprocessing();

console/dbscript/localdb.sql

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
USE [ContosoEdw]
2+
GO
3+
4+
SET ANSI_NULLS ON
5+
GO
6+
SET QUOTED_IDENTIFIER ON
7+
GO
8+
CREATE TABLE [dbo].[AutoInvoiceHeader](
9+
[InvoiceNumber] [nvarchar](50) NOT NULL,
10+
[SupplierCode] [nvarchar](10) NOT NULL,
11+
[InvoiceStatus] [nvarchar](50) NOT NULL,
12+
CONSTRAINT [PK_AutoInvoiceHeader] PRIMARY KEY CLUSTERED
13+
(
14+
[InvoiceNumber] ASC,
15+
[SupplierCode] ASC
16+
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
17+
) ON [PRIMARY]
18+
GO
19+
20+
SET ANSI_NULLS ON
21+
GO
22+
SET QUOTED_IDENTIFIER ON
23+
GO
24+
CREATE TABLE [dbo].[PurchaseOrder](
25+
[SAPPONumber] [nvarchar](50) NOT NULL,
26+
[PurchaseOrderStatus] [nvarchar](50) NOT NULL,
27+
CONSTRAINT [PK_PurchaseOrder] PRIMARY KEY CLUSTERED
28+
(
29+
[SAPPONumber] ASC
30+
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
31+
) ON [PRIMARY]
32+
GO
33+
34+
SET ANSI_NULLS ON
35+
GO
36+
SET QUOTED_IDENTIFIER ON
37+
GO
38+
CREATE TABLE [dbo].[Supplier](
39+
[SupplierCode] [nvarchar](10) NOT NULL,
40+
[Name] [nvarchar](500) NOT NULL,
41+
CONSTRAINT [PK_Suppliers] PRIMARY KEY CLUSTERED
42+
(
43+
[SupplierCode] ASC
44+
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
45+
) ON [PRIMARY]
46+
GO
47+
48+
INSERT [dbo].[AutoInvoiceHeader] ([InvoiceNumber], [SupplierCode], [InvoiceStatus]) VALUES (N'11001', N'1234000', N'Processing')
49+
GO
50+
INSERT [dbo].[AutoInvoiceHeader] ([InvoiceNumber], [SupplierCode], [InvoiceStatus]) VALUES (N'11001', N'149033', N'InReview')
51+
GO
52+
INSERT [dbo].[AutoInvoiceHeader] ([InvoiceNumber], [SupplierCode], [InvoiceStatus]) VALUES (N'11002', N'149033', N'Paid')
53+
GO
54+
INSERT [dbo].[AutoInvoiceHeader] ([InvoiceNumber], [SupplierCode], [InvoiceStatus]) VALUES (N'11003', N'120010', N'Pending')
55+
GO
56+
INSERT [dbo].[PurchaseOrder] ([SAPPONumber], [PurchaseOrderStatus]) VALUES (N'11001', N'InReview')
57+
GO
58+
INSERT [dbo].[PurchaseOrder] ([SAPPONumber], [PurchaseOrderStatus]) VALUES (N'11002', N'Released')
59+
GO
60+
INSERT [dbo].[PurchaseOrder] ([SAPPONumber], [PurchaseOrderStatus]) VALUES (N'4300000009', N'Closed')
61+
GO
62+
INSERT [dbo].[Supplier] ([SupplierCode], [Name]) VALUES (N'120010', N'ABC Company')
63+
GO
64+
INSERT [dbo].[Supplier] ([SupplierCode], [Name]) VALUES (N'1233100', N'Contestant TV')
65+
GO
66+
INSERT [dbo].[Supplier] ([SupplierCode], [Name]) VALUES (N'1233200', N'Connection Consultants')
67+
GO
68+
INSERT [dbo].[Supplier] ([SupplierCode], [Name]) VALUES (N'1233300', N'Contestant House')
69+
GO
70+
INSERT [dbo].[Supplier] ([SupplierCode], [Name]) VALUES (N'1233400', N'Contoso XYZ')
71+
GO
72+
INSERT [dbo].[Supplier] ([SupplierCode], [Name]) VALUES (N'1233500', N'Contestant Funds')
73+
GO
74+
INSERT [dbo].[Supplier] ([SupplierCode], [Name]) VALUES (N'1234000', N'Contoso Space')
75+
GO
76+
INSERT [dbo].[Supplier] ([SupplierCode], [Name]) VALUES (N'1234500', N'Contestant Network')
77+
GO
78+
INSERT [dbo].[Supplier] ([SupplierCode], [Name]) VALUES (N'1234600', N'Contoso LLC')
79+
GO
80+
INSERT [dbo].[Supplier] ([SupplierCode], [Name]) VALUES (N'1234700', N'Contestant Ltd')
81+
GO
82+
INSERT [dbo].[Supplier] ([SupplierCode], [Name]) VALUES (N'1234800', N'Connections Inc.')
83+
GO
84+
INSERT [dbo].[Supplier] ([SupplierCode], [Name]) VALUES (N'1234900', N'Contestant Holdings')
85+
GO
86+
INSERT [dbo].[Supplier] ([SupplierCode], [Name]) VALUES (N'134510', N'XYZ Company Ltd')
87+
GO
88+
INSERT [dbo].[Supplier] ([SupplierCode], [Name]) VALUES (N'149033', N'Contoso Inc')
89+
GO

console/email/auth.js

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
const fs = require('fs');
2+
const tokenFile = "./email/apptokens.json";
3+
4+
const credentials = {
5+
client: {
6+
id: process.env.APP_ID,
7+
secret: process.env.APP_PASSWORD,
8+
},
9+
auth: {
10+
tokenHost: 'https://login.microsoftonline.com',
11+
authorizePath: 'common/oauth2/v2.0/authorize',
12+
tokenPath: 'common/oauth2/v2.0/token'
13+
}
14+
};
15+
16+
const oauth2 = require('simple-oauth2').create(credentials);
17+
const jwt = require('jsonwebtoken');
18+
19+
function getAuthUrl() {
20+
const returnVal = oauth2.authorizationCode.authorizeURL({
21+
redirect_uri: process.env.REDIRECT_URI,
22+
scope: process.env.APP_SCOPES
23+
});
24+
console.log(`Generated auth url: ${returnVal}`);
25+
return returnVal;
26+
}
27+
28+
async function getAccessTokenFromCode(auth_code) {
29+
let result = await oauth2.authorizationCode.getToken({
30+
code: auth_code,
31+
redirect_uri: process.env.REDIRECT_URI,
32+
scope: process.env.APP_SCOPES
33+
});
34+
35+
const token = oauth2.accessToken.create(result);
36+
console.log('Token created: ', token.token);
37+
38+
saveToken(token);
39+
40+
return token.token.access_token;
41+
}
42+
43+
async function getAccessToken() {
44+
45+
var appTokens = JSON.parse(fs.readFileSync(tokenFile, 'utf8'));
46+
var token = appTokens.token;
47+
var user = jwt.decode(token.id_token);
48+
49+
// Check if token is expired ?
50+
if(token.access_token){
51+
const FIVE_MINUTES = 300000; // Expire 5 minutes early to account for clock differences
52+
var tokenExpirationTime = new Date(token.expires_at).getTime();
53+
const expiration = new Date(parseFloat(tokenExpirationTime - FIVE_MINUTES));
54+
if (expiration > new Date()) {
55+
console.log("Token not Expired.")
56+
return {
57+
"accesstoken": token.access_token,
58+
"username" : user.name
59+
};
60+
}
61+
}
62+
63+
// Get new token from refresh_token
64+
const refresh_token = token.refresh_token;
65+
if (refresh_token) {
66+
console.log("Token Expired. Getting new token using Refresh token.");
67+
//console.log(refresh_token);
68+
const newToken = await oauth2.accessToken.create({ refresh_token: refresh_token }).refresh();
69+
saveToken(newToken);
70+
return {
71+
"accesstoken": newToken.token.access_token,
72+
"username" : user.name
73+
};
74+
}
75+
}
76+
77+
function saveToken(token){
78+
fs.writeFile(tokenFile, JSON.stringify(token), function (err) {
79+
if (err) {
80+
return console.log(err);
81+
}
82+
console.log("Tokens Saved");
83+
});
84+
}
85+
86+
exports.getAuthUrl = getAuthUrl;
87+
exports.getAccessTokenFromCode = getAccessTokenFromCode;
88+
exports.getAccessToken = getAccessToken;
89+
exports.tokenFile = tokenFile;

console/email/index.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
const authHelper = require('./auth');
2+
const graph = require('@microsoft/microsoft-graph-client');
3+
var logger = require("../logger");
4+
async function replyMessage(msg, callback) {
5+
var token = await authHelper.getAccessToken();
6+
var client = graph.Client.init({
7+
authProvider: (done) => {
8+
done(null, token.accesstoken);
9+
}
10+
});
11+
var emailBody = "Hello, </br></br>";
12+
if(msg.messageType == 1) {
13+
emailBody += "Payment status for " + msg.invoiceNumber + " and vendor " + msg.vendorName + " is : " + msg.status;
14+
} else if(msg.messageType == 2){
15+
emailBody += "PO status for " + msg.invoiceNumber + " is : " + msg.status;
16+
} else if(msg.messageType == 3){
17+
emailBody += "Here is the link to download the copy of your invoice : <a href='" + msg.downloadLink +"'>" + msg.invoiceNumber + "</a>";
18+
}
19+
emailBody += "<br/><br/>Thank you, <br/><br/>Contoso AP Bot"
20+
21+
const mail = {
22+
subject: "Ticket " + msg.serviceNowTicketNumber + " has been Resolved",
23+
toRecipients: [{
24+
emailAddress: {
25+
address: msg.replyTo
26+
}
27+
}],
28+
body: {
29+
content: emailBody,
30+
contentType: "html"
31+
}
32+
}
33+
await client.api('/users/me/sendMail').post({ message: mail });
34+
logger.log("=================> Email sent to : " + msg.replyTo + "\n")
35+
};
36+
exports.replyMessage = replyMessage;
37+

0 commit comments

Comments
 (0)