Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Google/quick add #4

Merged
merged 48 commits into from
Aug 16, 2019
Merged
Show file tree
Hide file tree
Changes from 47 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
2a809a0
Login branch initial commit
Gautime May 28, 2019
2c27edf
Basic Message Printing
Gautime May 28, 2019
140654e
Login/Logout Api Call
Gautime May 29, 2019
7ba90b4
Resolved minor errors
Gautime May 30, 2019
3f7f3e4
Improved url
Gautime May 30, 2019
43f6f85
Changed response_type
Gautime May 30, 2019
2aaecca
Added the auth link as the message
Gautime May 30, 2019
86cf471
Webhook Endpoint
Gautime Jun 4, 2019
372ec52
Changed workflow
Gautime Jun 5, 2019
29a9bf1
Corrected typo
Gautime Jun 5, 2019
e824d92
Add token id to app persistence
Gautime Jun 5, 2019
f49b6b5
Updated files
Gautime Jun 5, 2019
c1e3a6f
Changed Webhook method
Gautime Jun 5, 2019
1203322
Updated api call
Gautime Jun 5, 2019
981ddf0
Access token received
Gautime Jun 6, 2019
9a4a156
Included user credentials, success login message
Gautime Jun 7, 2019
5b1caf8
Added access token in app persistence and updated Readme
Gautime Jun 10, 2019
34d0d0d
Updated persistence
Gautime Jun 10, 2019
01c699a
Update README.md
Gautime Jun 10, 2019
b25c4f6
Logged access token inside GCGetter
Gautime Jun 10, 2019
79ec838
Merge remote-tracking branch 'Apps.Google.Calendar/google/login' into…
Gautime Jun 10, 2019
b3d7ee8
Added logout feature
Gautime Jun 12, 2019
4cc39db
View Event call added
Gautime Jun 12, 2019
1e888ed
Display title, start and end time of events
Gautime Jun 13, 2019
9d952ee
Date - Time format changed
Gautime Jun 14, 2019
ce1d552
Create event implemented
Gautime Jun 15, 2019
f81ab63
Error message if event creation fails
Gautime Jun 15, 2019
0e261ea
Added redirect uri config setting
Gautime Jun 18, 2019
7ea1aa4
Added link with each view event for update/delete
Gautime Jun 19, 2019
3273112
Update README.md
Gautime Jun 19, 2019
2fd9de4
Update README.md
Gautime Jun 19, 2019
fd8c5a4
Merge branch 'master' into google/create-private
Gautime Jun 22, 2019
af44f54
Resolved conflicts
Gautime Jun 23, 2019
1811ffd
Merge branch 'master' into google/create-private
Gautime Jun 24, 2019
893fa3f
Create LICENSE
Gautime Jun 24, 2019
f423c23
Quickadd Added
Gautime Jul 3, 2019
310ccb7
Changed naming convention
Gautime Jul 3, 2019
5d1f9e2
Update README.md
Gautime Jul 3, 2019
6743748
Merge branch 'master' into google/quick-add
Gautime Jul 3, 2019
ec2b555
Update README.md
Gautime Jul 3, 2019
5fcda42
Updated changes
Gautime Jul 4, 2019
1c8429c
Merge remote-tracking branch 'Apps.Google.Calendar/google/quick-add' …
Gautime Jul 4, 2019
ad4f65d
Updated persistence
Gautime Jul 5, 2019
6425639
Fix typo on variable name
d-gubert Jul 13, 2019
61fff55
Revert "Fix typo on variable name"
d-gubert Jul 13, 2019
ada1154
Merge branch 'master' into google/quick-add
Gautime Jul 26, 2019
36cd28b
Merge branch 'master' into google/quick-add
Gautime Jul 26, 2019
1cbc944
Google/calendar list (#5)
Gautime Aug 16, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Commands/GCCommands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export class GCCommand implements ISlashCommand {
try {


const loginstatus = await this.app.getGCGetter().login(this.app.getLogger(), read, http, modify, context, persis);
const login_status = await this.app.getGCGetter().login(this.app.getLogger(), read, http, modify, context, persis);

msg.setText('Slashcommand executed');
// await modify.getCreator().finish(msg);
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,13 @@ Integrates google calendar with your Rocket.Chat server.
* Get reminders and notifications of events.
* Create public events inside a room inviting all the users inside the room.

## How to use it?
* Download or clone the repository to your local system.
* Get your local Rocket.Chat server running.
* Navigate inside the folder using terminal.
* Run command `rc-apps deploy --url http://localhost:3000 --username {your_username} --password {your_password}`
* This gets the app installed in your local server, now navigate to app screen inside Options -> Administration -> Apps -> Google Calendar
* Activate the app and put in the credentials and start using it inside any of your room!

## Quick Start Guide

Expand All @@ -21,7 +28,16 @@ Integrates google calendar with your Rocket.Chat server.

* `/calendar logout` : Once you are done with viewing, creating your calendar events and wants to log out of the gmail account, use this command and it will log you out and redirect to your home page.

* `/calendar view` : Once the authentication is successful, you can use this command to view the private events on your calendar. Events will be displayed with title, date, start time and end time. You will also get the link, which you can click on and it will take directly to your calendar where you can udpate or delete that event.

* `/calendar create "Title" "Date" "Start time" "Endtime"` : This command can be used to create a private on your primary calendar, where Title is the title of the event you are creating, and date should be in format YYYY-MM-DD and time in 24 hours format.

* `/calendar logout` : Once you are done with viewing, creating your calendar events and wants to log out of the gmail account, use this command and it will log you out and redirect to your home page.

* `/calendar quickadd {title of the event}` - This slashcommand can be used to create an event starting from time the slashcommand is called to one hour in duration.


### Feedback and Suggestions
Contribute to this repository by opening an issue if you have any feedback or suggestions for improvements or even some feature request!


8 changes: 4 additions & 4 deletions helpers/GCResult.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,20 @@ import { HttpStatusCode, IHttp, ILogger, IRead } from '@rocket.chat/apps-engine/
import { IApiRequest } from '@rocket.chat/apps-engine/definition/api';

export class GCResults {
public atoken: string;
public acess_token: string;
Gautime marked this conversation as resolved.
Show resolved Hide resolved

constructor(data?: any) {

if (data) {
this.atoken = data.access_token as string;
this.acess_token = data.access_token as string;
}
}

public result(): string {
if (!this.atoken) {
if (!this.acess_token) {
throw new Error('Invalid result');
}
return this.atoken;
return this.acess_token;
}

}
106 changes: 64 additions & 42 deletions helpers/GSGetter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ enum Command {
lgout = 'logout',
show = 'view',
make = 'create',
}
quick = 'quickadd',



export class GCGetter {
Expand All @@ -23,49 +24,53 @@ export class GCGetter {
public async login(logger: ILogger, read: IRead, http: IHttp, modify: IModify, context: SlashCommandContext, persis: IPersistence): Promise<void> {


const Client_id = await read.getEnvironmentReader().getSettings().getValueById('calendar_clientid');
const client_id = await read.getEnvironmentReader().getSettings().getValueById('calendar_clientid');
const api_key = await read.getEnvironmentReader().getSettings().getValueById('calendar_apikey');
const secret = await read.getEnvironmentReader().getSettings().getValueById('calendar_secret_key');

const redirect = await read.getEnvironmentReader().getSettings().getValueById('redirect_uri');
const persistence = new AppPersistence(persis, read.getPersistenceReader());
const id = await persistence.connectUserToClient(Client_id, context.getSender());
const id = await persistence.connectUserToClient(client_id, context.getSender());


let signedin: boolean = false;
const msg = modify.getCreator().startMessage().setSender(context.getSender()).setRoom(context.getRoom());

const message = modify.getCreator().startMessage().setSender(context.getSender()).setRoom(context.getRoom());

const [parameter] = context.getArguments();

const [parame] = context.getArguments();
switch (parameter) {

switch (parame) {

case (Command.connect):
const response = (`${this.urli}client_id=${Client_id}&redirect_uri=${redirect}/api/apps/public/c759c4f1-a3c1-4202-8238-c6868633ed87/webhook&scope=https://www.googleapis.com/auth/calendar&prompt=consent&access_type=offline&response_type=code`);
const response = (`${this.urli}client_id=${client_id}&redirect_uri=${redirect}/api/apps/public/c759c4f1-a3c1-4202-8238-c6868633ed87/webhook&scope=https://www.googleapis.com/auth/calendar&prompt=consent&access_type=offline&response_type=code`);


try {
msg.setText(response);
await modify.getCreator().finish(msg);
message.setText(response);
await modify.getCreator().finish(message);
} catch (e) {
this.app.getLogger().error('Failed sending login url', e);
msg.setText('An error occurred when trying to send the login url:disappointed_relieved:');
message.setText('An error occurred when trying to send the login url:disappointed_relieved:');

modify.getNotifier().notifyUser(context.getSender(), msg.getMessage());
modify.getNotifier().notifyUser(context.getSender(), message.getMessage());

}
break;

case (Command.lgout):

const logresponse = `https://www.google.com/accounts/Logout?continue=https://appengine.google.com/_ah/logout?continue=${redirect}`;
// const mesg = modify.getCreator().startMessage().setSender(context.getSender()).setRoom(context.getRoom());
const logresponse = `https://www.google.com/accounts/Logout?continue=${redirect}`;

try {
msg.setText(logresponse);
await modify.getCreator().finish(msg);
message.setText(logresponse);
await modify.getCreator().finish(message);
} catch (e) {
this.app.getLogger().error('Failed sending logout url', e);
msg.setText('An error occurred when trying to send the logout url:disappointed_relieved:');
message.setText('An error occurred when trying to send the logout url:disappointed_relieved:');

modify.getNotifier().notifyUser(context.getSender(), message.getMessage());

modify.getNotifier().notifyUser(context.getSender(), msg.getMessage());
}


Expand All @@ -77,76 +82,93 @@ export class GCGetter {

case (Command.show):

const newatoken = await persistence.getAT(context.getSender());
const new_token = await persistence.getAT(context.getSender());

const dat = new Date();
const newdate = dat.toISOString();
const url = `https://www.googleapis.com/calendar/v3/calendars/primary/events?key=${api_key}&showDeleted=false&timeMin=${newdate}`;
const newresponse = await http.get(url, { headers: { 'Authorization': `Bearer ${newatoken}`, } });
const minimum_date = dat.toISOString();
const url = `https://www.googleapis.com/calendar/v3/calendars/primary/events?key=${api_key}&showDeleted=false&timeMin=${minimum_date}`;
const api_response = await http.get(url, { headers: { 'Authorization': `Bearer ${new_token}`, } });

for (var i = 0; i < newresponse.data.items.length; i++) {
for (var i = 0; i < api_response.data.items.length; i++) {
// console.log( newresponse.data.items[i].summary);
await displayevents(newresponse.data.items[i], modify, context);
await displayevents(api_response.data.items[i], modify, context);

}

break;

case (Command.make):

const createatoken = await persistence.getAT(context.getSender());
const access_token = await persistence.getAT(context.getSender());
const params = context.getArguments().join(' ');
const array = params.split("\"");
const createurl = `https://www.googleapis.com/calendar/v3/calendars/primary/events?key=${api_key}`;
const create_url = `https://www.googleapis.com/calendar/v3/calendars/primary/events?key=${api_key}`;
console.log('Create event array elements are these:', array[1], array[3]);

const datetime = array[3] + 'T' + array[5];
const date = new Date(datetime);
//const starttime = new Date(date.getTime() - date.getTimezoneOffset() * 60000);
const startdatetime = date.toISOString();
const start_datetime = date.toISOString();

const edate = array[3] + 'T' + array[7];
const enddate = new Date(edate);
const enddatetime = enddate.toISOString();
const e_date = array[3] + 'T' + array[7];
const end_date = new Date(e_date);
const end_datetime = end_date.toISOString();


// console.log('Start date and time in ISO format is: ',startdatetime,'end date time:',enddatetime);

const createresponse = await http.post(createurl, { headers: { 'Authorization': `Bearer ${createatoken}`, }, data: { 'summary': `${array[1]}`, 'end': { 'dateTime': `${enddatetime}`, }, 'start': { 'dateTime': `${startdatetime}` } } });
console.log('This is the create event request response: ', createresponse);
if (createresponse.statusCode == HttpStatusCode.OK && createresponse.data.status == "confirmed") { //console.log('Event created wohoooooo!!!');
const create_api_response = await http.post(create_url, { headers: { 'Authorization': `Bearer ${access_token}`, }, data: { 'summary': `${array[1]}`, 'end': { 'dateTime': `${end_datetime}`, }, 'start': { 'dateTime': `${start_datetime}` } } });
console.log('This is the create event request response: ', create_api_response);
if (create_api_response.statusCode == HttpStatusCode.OK && create_api_response.data.status == "confirmed") { //console.log('Event created wohoooooo!!!');
try {
msg.addAttachment({
message.addAttachment({

text: `Event has been created. Find the event at [${createresponse.data.summary}](${createresponse.data.htmlLink}) `,
text: `Event has been created. Find the event at [${create_api_response.data.summary}](${create_api_response.data.htmlLink}) `,


});
await modify.getCreator().finish(msg);
await modify.getCreator().finish(message);
} catch (e) {
this.app.getLogger().error('Failed creating events', e);
msg.setText('An error occurred when sending the event creation as message :disappointed_relieved:');
message.setText('An error occurred when sending the event creation as message :disappointed_relieved:');
}
}
else {
console.log('This is the error message:', createresponse.data.error.message);
console.log('This is the error message:', create_api_response.data.error.message);

try {
msg.addAttachment({
message.addAttachment({

text: `Event could not be created. It encountered the error: ${createresponse.data.error.message}. Please try again. `,
text: `Event could not be created. It encountered the error: ${create_api_response.data.error.message}. Please try again. `,


});
await modify.getCreator().finish(msg);
await modify.getCreator().finish(message);
} catch (e) {
this.app.getLogger().error('Failed creating events', e);
msg.setText('An error occurred when sending the event creation as message :disappointed_relieved:');
message.setText('An error occurred when sending the event creation as message :disappointed_relieved:');
}
}
break;
}

case (Command.quick):

const title = context.getArguments().join(' ');
const title_new = title.split('\"');
//const fintitle = titlenew.;
const token = await persistence.getAT(context.getSender());
const quick_url = `https://www.googleapis.com/calendar/v3/calendars/primary/events/quickAdd?key=${api_key}&text=${title_new[1]}`;
const quick_api_response = await http.post(quick_url, { headers: { 'Authorization': `Bearer ${token}`, } });
console.log('This is the quick-add response', quick_api_response);
if (quick_api_response && quick_api_response.statusCode == HttpStatusCode.OK) {
// const msg = modify.getCreator().startMessage().setSender(context.getSender()).setRoom(context.getRoom());
message.setText('Quickadd event succcessfully created!');
await modify.getCreator().finish(message);
}
break;
}
}



}
20 changes: 10 additions & 10 deletions helpers/Webhook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,31 +16,31 @@ export class WebhookEndpoint extends ApiEndpoint {

public async get(request: IApiRequest, endpoint: IApiEndpointInfo, read: IRead, modify: IModify, http: IHttp, persist: IPersistence): Promise<IApiResponse> {

const Client_id = await read.getEnvironmentReader().getSettings().getValueById('calendar_clientid');
const client_id = await read.getEnvironmentReader().getSettings().getValueById('calendar_clientid');
const secret = await read.getEnvironmentReader().getSettings().getValueById('calendar_secret_key');
const api_key = await read.getEnvironmentReader().getSettings().getValueById('calendar_apikey');

//logger.debug('response from first request is:', request.params);`

const auth_code = request.query.code;
const url = 'https://www.googleapis.com/oauth2/v4/token';
const newresponse = await http.post(url, { data: { 'code': `${auth_code}`, 'client_id': `${Client_id}`, 'client_secret': `${secret}`, 'redirect_uri': `${this.urli}`, 'grant_type': 'authorization_code', } });
const new_response = await http.post(url, { data: { 'code': `${auth_code}`, 'client_id': `${client_id}`, 'client_secret': `${secret}`, 'redirect_uri': `${this.urli}`, 'grant_type': 'authorization_code', } });

if (newresponse.statusCode !== HttpStatusCode.OK || !newresponse.data) {
console.log('Did not get a valid response', newresponse);
if (new_response.statusCode !== HttpStatusCode.OK || !new_response.data) {
console.log('Did not get a valid response', new_response);
throw new Error('Unable to retrieve response with auth code.');
}

console.log('This is the response from post api', newresponse);
const acesstoken = new GCResults(newresponse.data);
console.log('This is the response from post api', new_response);
const acess_token = new GCResults(new_response.data);
// logger.debug('Auth token received is: ', auth_code);
const atoken = acesstoken;
const atoken = acess_token;
const persistence = new AppPersistence(persist, read.getPersistenceReader());
const uid = await persistence.getuid(Client_id);
const id = await persistence.connectUserToAT(acesstoken, uid);
const user_id = await persistence.getuid(client_id);
const id = await persistence.connectUserToAT(acess_token, user_id);


if (acesstoken) {
if (acess_token) {
//location.assign('http://localhost:3000/home');
return this.success('<html><body><script type="text/javascript"></script><div>Sign-in successful! Please close this window/tab and continue using!</div></body></html>');
}
Expand Down
20 changes: 10 additions & 10 deletions helpers/persistence.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,29 @@ export class AppPersistence {
constructor(private readonly persistence: IPersistence, private readonly persistenceRead: IPersistenceRead) { }

public async connectUserToClient(clientid: string, user: IUser): Promise<void> {
const userAssociation = new RocketChatAssociationRecord(RocketChatAssociationModel.ROOM, user.id);
const clientAssociation = new RocketChatAssociationRecord(RocketChatAssociationModel.MISC, `${clientid}`);
const user_association = new RocketChatAssociationRecord(RocketChatAssociationModel.ROOM, user.id);
const client_association = new RocketChatAssociationRecord(RocketChatAssociationModel.MISC, `${clientid}`);

await this.persistence.updateByAssociations([userAssociation, clientAssociation], { uid: user.id }, true);
await this.persistence.updateByAssociations([user_association, client_association], { uid: user.id }, true);
}

public async getuid(Clientid: string): Promise<string> {

const clientAssociation = new RocketChatAssociationRecord(RocketChatAssociationModel.MISC, Clientid);
const [result] = await this.persistenceRead.readByAssociations([clientAssociation]);
const client_association = new RocketChatAssociationRecord(RocketChatAssociationModel.MISC, Clientid);
const [result] = await this.persistenceRead.readByAssociations([client_association]);
return result ? (result as any).uid : undefined;
}
public async connectUserToAT(atoken: any, user: string): Promise<void> {
const userAssociation = new RocketChatAssociationRecord(RocketChatAssociationModel.USER, user);
const user_association = new RocketChatAssociationRecord(RocketChatAssociationModel.USER, user);

await this.persistence.updateByAssociations([userAssociation], { atoken }, true);
await this.persistence.updateByAssociations([user_association], { atoken }, true);

}
public async getAT(user: IUser): Promise<void> {
const userAssociation = new RocketChatAssociationRecord(RocketChatAssociationModel.USER, user.id);
const user_association = new RocketChatAssociationRecord(RocketChatAssociationModel.USER, user.id);

const [result] = await this.persistenceRead.readByAssociation(userAssociation);
return result ? (result as any).atoken.atoken : undefined;
const [result] = await this.persistenceRead.readByAssociation(user_association);
return result ? (result as any).atoken.acess_token : undefined;

}
}
Loading