'use strict';
const request = require('request');
var soap = require('soap');
const axios = require('axios');
const moment = require("moment");
//https://nodemailer.com/about/
const nodemailer = require("nodemailer");
//https://github.com/aakatev/bigbluebutton-js
const bbb = require('bigbluebutton-js');
const xml2js = require('xml2js');
const url = require("url");
const fs = require("fs");
module.exports = {
async expiredUserSession(user) {
return true;
},
async sendBlackListMessage(user,appName){
const data = fs.readFileSync(`resources/emailTemplate/BlackListAlert.html`, 'utf8');
const html = data.replace(/APP_NAME/g, appName).replace(/USER_FULL_NAME/g,user.name)
.replace(/USER_PHONE_NO/g,user.phoneNo);
this.sendEmail(user.email, "هشدار!!!", html).then();
const inputData= [
{"software":appName},
{"name": user.name}
];
this.sendSms(user.phoneNo,'bheqcyif4gkqde0',inputData).then();
},
async getBBBMeetings() {
let api = bbb.api(
process.env.BBB_URL,
process.env.BBB_SECRET
);
const url = api.monitoring.getMeetings();
const result = await bbb.http(url);
return result;
},
async checkVersion(ctx){
const queryObject = url.parse(ctx.request.url, true).query;
const {version} = queryObject;
if (!version || version < process.env.CLIENT_NEED_VERSION){
return false;
}else{
ctx.request.url = ctx.request.url.replace("?version="+version,"");
return true;
}
},
async isAdobeRunning(levelId, returnLink = false) {
return false; // TODO:: until adobe be used again
let lastSession = await strapi.services.session.findOne({
course_level: levelId,
_sort: "updatedAt:DESC",
_limit: 1
});
if (!lastSession || !lastSession.isAdobe || !lastSession.internalMeetingID || lastSession.isPublished) {
return false;
}
const instance = axios.create({
baseURL: `${process.env.AC_SERVER}`,
withCredentials: true
});
let apiCall = `/api/xml?action=login&login=${process.env.AC_USER}&password=${process.env.AC_PASSWORD}`;
try {
let apiResp = await instance.get(apiCall, {withCredentials: true});
instance.defaults.headers = {
Cookie: apiResp.headers["set-cookie"][0]
};
apiResp = await strapi.services.helpers.xml2Json(apiResp.data);
apiResp = JSON.parse(apiResp);
if (apiResp && apiResp.results && apiResp.results.status[0].$.code === "ok") {
apiCall = `/api/xml?action=sco-contents&sco-id=${process.env.AC_MEETING_FOLDER}&filter-url-path=${lastSession.internalMeetingID}`;
let apiResp = await instance.get(encodeURI(apiCall), {withCredentials: true});
if (apiResp && apiResp.data) {
apiResp = await strapi.services.helpers.xml2Json(apiResp.data);
apiResp = JSON.parse(apiResp);
if (apiResp && apiResp.results && apiResp.results.status[0].$.code === "ok") {
let scoId = apiResp.results.scos[0].sco[0].$["sco-id"];
apiCall = `/api/xml?action=report-my-meetings&filter-sco-id=${scoId}`;
apiResp = await instance.get(encodeURI(apiCall), {withCredentials: true});
if (apiResp && apiResp.data) {
apiResp = await strapi.services.helpers.xml2Json(apiResp.data);
apiResp = JSON.parse(apiResp);
if (apiResp && apiResp.results && apiResp.results.status[0].$.code === "ok") {
const info = apiResp.results["my-meetings"][0].meeting[0].$;
if (Number(info["active-participants"])) {
if (returnLink) {
return `${process.env.AC_SERVER}${lastSession.internalMeetingID.replace(/\//g, '')}`;
}
return true;
}
}
}
}
}
return false;
}
} catch (ex) {
console.error("isAdobeRunning", ex);
return false;
}
},
async isBBBMeetingRunning(id) {
try {
const api = bbb.api(
process.env.BBB_URL,
process.env.BBB_SECRET
);
const url = api.monitoring.isMeetingRunning(id);
const result = await bbb.http(url);
return result && result.returncode === "SUCCESS" && result.running;
} catch (ex) {
strapi.services["system-log"].create({
type: "checkBBBIsRunning",
message: ex
});
return false;
}
},
async getBBBPublicSessions(isPrivate,isLogin=false) {
let api = bbb.api(
process.env.BBB_URL,
process.env.BBB_SECRET
);
const url = api.monitoring.getMeetings();
const result = await bbb.http(url);
let publics = [];
const noMeeting = result.messageKey === "noMeetings";
if (!noMeeting && result.returncode === "SUCCESS" && result.meetings && result.meetings.meeting) {
const isArray = Array.isArray(result.meetings.meeting);
if (isArray) {
if (isPrivate) {
publics = result.meetings.meeting.filter(m => m.metadata.isprivate && m.running);
} else {
publics = result.meetings.meeting.filter(m => (m.metadata.ispublic || m.metadata.ispublicregistred) && m.running);
}
} else if (!isPrivate && (result.meetings.meeting.metadata.ispublic || result.meetings.meeting.metadata.ispublicregistred)) {
publics = [result.meetings.meeting];
} else if (isPrivate && result.meetings.meeting.metadata.isprivate) {
publics = [result.meetings.meeting];
}
}
return publics;
},
async deleteBBBRecordings(id) {
let api = bbb.api(
process.env.BBB_URL,
process.env.BBB_SECRET
);
const url = api.recording.deleteRecordings(id);
return await bbb.http(url);
},
async getBBBRecordings() {
let api = bbb.api(
process.env.BBB_URL,
process.env.BBB_SECRET
);
const url = api.recording.getRecordings();
return await bbb.http(url);
},
isEmail(email) {
const emailRegExp = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return emailRegExp.test(email);
},
async makeUserIdForBBBSession(email, phoneNo, ip) {
return [email, phoneNo, ip].join(" - ");
},
async verifyBBBUserNotJoined(meetingInfo, userId) {
if (meetingInfo && meetingInfo.attendees && meetingInfo.attendees.attendee && meetingInfo.attendees.attendee.length) {
const exist = meetingInfo.attendees.attendee.find(x => x.userID === userId);
if (exist) {
return {
result: false,
entity: exist
}
}
}
return {
result: true,
entity: null
};
},
async getBBBMeetingInfo(id) {
let api = bbb.api(
process.env.BBB_URL,
process.env.BBB_SECRET
);
const url = api.monitoring.getMeetingInfo(id);
const result = await bbb.http(url);
return result;
},
async getBBBMeetingInfoByInternalId(id) {
const userSessions = await strapi.services.session.find({internalMeetingID: id});
if (!userSessions || userSessions.length !== 1) {
return {result: false, entity: "session.errors.invalidMeetingId"};
}
const userSession = userSessions[0];
let api = bbb.api(
process.env.BBB_URL,
process.env.BBB_SECRET
);
const meetingId= userSession.course_level? userSession.course_level.id: userSession.id;
const url = api.monitoring.getMeetingInfo(meetingId);
const result = await bbb.http(url);
return {result:true,entity:result};
},
async createBBBMeeting(name, id, params) {
let api = bbb.api(
process.env.BBB_URL,
process.env.BBB_SECRET
);
const meetingCreateUrl = api.administration.create(name, id, params);
return await bbb.http(meetingCreateUrl);
},
async joinUserToMeeting(fullName, meetingId, password, userID, guest = true) {
let api = bbb.api(
process.env.BBB_URL,
process.env.BBB_SECRET
);
const kwparams = {userID};
if (guest) {
kwparams.guest = guest;
}
let userUrl = api.administration.join(fullName, meetingId, password, kwparams);
return userUrl;
},
async endJoin(user, meetingID, password) {
let api = bbb.api(
process.env.BBB_URL,
process.env.BBB_SECRET
);
let endUrl = api.administration.end(meetingID, password);
},
async joinBBB(user) {
let api = bbb.api(
process.env.BBB_URL,
process.env.BBB_SECRET
);
let http = bbb.http;
// api module itslef is responsible for constructing URLs
let meetingCreateUrl = api.administration.create('My Meeting Number 1', '1', {
attendeePW: 'secret',
moderatorPW: 'supersecret',
});
//let meetingEndUrl = api.administration.end('1', 'supersecret');
//console.log(`End meeting link: ${meetingEndUrl}`);
// http method should be used in order to make calls
let result = await http(meetingCreateUrl);
let moderatorUrl = api.administration.join('moderator', '1', 'supersecret',
{
user_id: 'papinaser',
redirect: "FALSE"
});
let result1 = await http(moderatorUrl);
let attendeeUrl = api.administration.join('attendee', '1', 'secret', {
user_id: 'user1',
redirect: "FALSE"
});
let result2 = await http(attendeeUrl);
return {
moderatorUrl: result1,
attendeeUrl: result2
};
},
makeRandomNumber(from, to) {
return Math.floor((Math.random() * to) + from);
},
async sendEmail(to, subject, html) {
// create reusable transporter object using the default SMTP transport
let transporter = nodemailer.createTransport({
host: process.env.SMTP_SERVER,
port: process.env.SMTP_PORT,
secure: false, // true for 465, false for other ports
auth: {
user: process.env.SMTP_USER, // generated ethereal user
pass: process.env.SMTP_PASS, // generated ethereal password
},
// here it goes
tls: {rejectUnauthorized: false},
});
// send mail with defined transport object
let info = await transporter.sendMail({
from: process.env.SMTP_SENDER, // sender address
to: to, //"bar@example.com, baz@example.com", // list of receivers
cc: process.env.MANAGER_EMAIL,
subject: subject,// "Hello ✔", // Subject line
//text: "Hello world?", // plain text body
html,
amp: html//"Hello world?", // html body
});
console.log("Message sent: %s", info.messageId);
// Message sent:
},
async xml2Json(xml) {
return new Promise(resolve => {
xml2js.parseString(xml, (err, result) => {
if (err) {
strapi.services["system-log"].create({
type: "xml2Json",
message: err.message
});
}
// `result` is a JavaScript object
// convert it to a JSON string
const json = JSON.stringify(result, null, 4);
// log JSON string
resolve(json);
});
});
},
async sendSms(phoneNo, patternCode, inputData) {
phoneNo = phoneNo.toString();
if (!phoneNo.startsWith("0")) {
phoneNo = "0" + phoneNo;
}
return new Promise((resolve) => {
request.post({
url: 'http://ippanel.com/api/select',
body: {
"op": "pattern",
"user": "09106116696",
"pass": "NabZ2020$",
"fromNum": "5000125475",
"toNum": phoneNo,
"patternCode": patternCode,
"inputData": inputData
},
json: true,
}, function (error, response, body) {
if (!error && response.statusCode === 200) {
//YOU CAN CHECK THE RESPONSE AND SEE ERROR OR SUCCESS MESSAGE
resolve({result: true, entity: response});
} else {
if (error) {
resolve({result: false, entity: error});
} else if (response.statusCode === 504) {
resolve({result: false, entity: "errors.timout"});
} else {
resolve({result: false, entity: "errors.serverSideError"});
}
}
});
});
},
async saveListEntity(name, list) {
const result = await list.reduce(async (memo, item) => {
const results = await memo;
const entity = await strapi.services[name].create(item);
return [...results, entity._id]
}, []);
return result;
},
reverseString(str) {
return str.split("").reverse().join("");
},
normalizePhoneNo(phoneNo) { //always return a number
if (phoneNo.length < 10 ||
(phoneNo.length > 11 && !phoneNo.startsWith("0")) ||
(phoneNo.length > 10 && phoneNo.startsWith("9")) ||
(phoneNo.length === 10 && !phoneNo.startsWith("9"))) {
return false;
} else {
return Number(phoneNo);
// if (phoneNo.startsWith("0")){
// return phoneNo;
// }
// return "0" + phoneNo;
}
},
async verifyPhoneNoExist(ctx, phoneNo) {
const user = await strapi.query('user', 'users-permissions').findOne({phoneNo});
if (!user) {
ctx.badRequest(
null,
"auth.errors.phoneNoNotExist"
);
return false;
}
return true;
},
isAdmin(user) {
return user.role === "AdvanceUser" || user.role.name === "AdvanceUser";
},
howMuchDaysLeft(toDate){
const now = moment(new Date()); //todays date
const end = moment(toDate); // another date
return Math.abs(now.diff(end, 'days'))+1;
},
isNowAfterDate(fromData){
const check = new Date();
return check.getTime() > fromData.getTime();
},
isNowBetweenDates(fromDate, toDate) {
const check = new Date();
return (check.getTime() <= toDate.getTime() && check.getTime() >= fromDate.getTime());
},
addMinutes(date, minutes) {
if (typeof date === "string") {
date = new Date(date)
}
return new Date(date.getTime() + minutes * 60000);
},
addSecond(date, sec) {
if (typeof date === "string") {
date = new Date(date)
}
return new Date(date.getTime() + sec * 1000);
},
sleep(second) {
return new Promise(resolve => {
setTimeout(resolve, second * 1000);
})
},
async getCourseLevels(levelId) {
const courseInfo = await strapi.services["course-level"].findOne({id: levelId});
const result = [];
if (courseInfo.isGeneral) {
const arr = courseInfo.course.course_levels.map(id => {
return new Promise(async resolve => {
const lInfo = await strapi.services["course-level"].findOne({id});
resolve(lInfo)
})
});
result.push(...await Promise.all(arr));
} else {
result.push(courseInfo);
}
return result;
},
async hasUserAccessToLevel(levelId, user) {
if (user.blocked) {
return false;
}
const levels = await this.getCourseLevels(levelId);
const arr = levels.map(level => {
return new Promise(async resolve => {
const uci = await strapi.services["user-course"]
.find({course_levels: [level.id], user: user.id, isConfirm: true});
resolve(uci && uci.length > 0);
});
});
const isValid = await Promise.all(arr);
return isValid.includes(true);
},
addDays(theDate, days) {
console.log("theDate",theDate);
return new Date(theDate.getTime() + days*24*60*60*1000);
},
async verifyUserAccessToMeeting(meetingId, user) {
const userSessions = await strapi.services.session.find({internalMeetingID: meetingId});
if (!userSessions || userSessions.length !== 1) {
return {result: false, entity: "session.errors.invalidMeetingId"};
}
const userSession = userSessions[0];
if (this.isAdmin(user)) {
return {result: true, entity: userSessions};
}
if (userSession.disallow_users && userSession.disallow_users.includes(user.id)) {
return {result: false, entity: "sessions.errors.userHasNoAccess"};
}
const levelGeneral = await strapi.services.course.findOne({
course_levels_in: [userSession.course_level.id]
});//may a level don't have general
const lIds = [userSession.course_level.id];
if (levelGeneral) {
const gl = await strapi.services["course-level"].findOne({
course: levelGeneral.id
});
if (gl) {
lIds.push(gl.id);
}
}
const uci = await strapi.services["user-course"]
.find({course_levels_in: lIds, user: user.id, isConfirm: true});
const hasAccess = uci && uci.length !== 0;
if (!hasAccess) {
return {result: false, entity: "sessions.errors.userHasNoAccess"};
}
return {result: true, entity: userSession};
},
async toDataURL(url) {
const file = await axios.get(url, {responseType: 'arraybuffer'});
let raw = Buffer.from(file.data).toString('base64');
return raw;
},
async verifyMellatPay (facId,amount,saleReferenceId){
const url = 'https://bpm.shaparak.ir/pgwchannel/services/pgw?wsdl';
const args = {terminalId: 6860806,userName:'optimyar88',userPassword:'26959182',
orderId:facId,
saleOrderId:facId,
saleReferenceId:saleReferenceId};
const options = {
overrideRootElement: {
namespace: 'ns1'
}
};
return new Promise((resolve)=>{
soap.createClient(url, options, (err, client) => {
if (err){
console.log("bpVerifyRequest Step1",err);
resolve({result:true,entity:{code:'VS1'}});
return;
}
client.bpVerifyRequest(args, (err, result, body) => {
if(err) {
console.log("requestForPayBP Step2",err);
resolve({result:true,entity:{code:'VS2'}});
}else{
console.log("result!!!!!!",result);
const arr = result? result.return.split(",") : [200];
if (arr[0]!=="0"){
console.log("requestForPayBP Step3",result);
client.bpVerifyRequest(args, (err, result2, body) => {
if(err) {
console.log("requestForPayBP Step3",err);
resolve({result:true,entity:{code:'VS3'}});
}else{
console.log("result repeat",result2);
const arr = result2? result2.return.split(",") : [300];
if (arr[0]!=="0" && arr[0]!=='43'){
resolve({result:true,entity:{code:'VS4'}});
}else {
resolve({result:true,entity:{code:101}});
}
}
})
}else{
console.log("result 1",result);
resolve({result:true,entity:{code:100}});
}
}
})
});
});
},
async verifyZarinpalPay(authority, amount) {
return new Promise(resolve => {
axios.post("https://api.zarinpal.com/pg/v4/payment/verify.json", {
merchant_id: "7527a218-5adb-401f-bd3e-72eee628b25d",//"ce442962-6b0f-4094-ae81-296bc6a5b5ea",
amount: amount * 10,//rial
authority
}).then(resp => {
resolve({result: true, entity: resp.data.data});
}).catch(err => {
resolve({result: true, entity: {code: err.response.data.errors.code}});
})
});
},
async requestForPayBP(amount,description, mobile, email,facId){
const url = 'https://bpm.shaparak.ir/pgwchannel/services/pgw?wsdl';
const localDate = moment().format('YYYYMMDD');
const localTime = moment().format('HHmmss');
const args = {terminalId: 6860806,userName:'optimyar88',userPassword:'26959182',
payerId:0,
orderId:facId,
callBackUrl:"https://api.optimyar.com/user-courses/paycallBack",
localDate,localTime,
amount:amount*10,mobileNo:mobile,additionalData:description};
const options = {
overrideRootElement: {
namespace: 'ns1'
}
};
// return {
// result:true,
// entity: {
// mobile:'989106116696',
// result:true,
// authority:'00000000000000'
// }
// };
return new Promise((resolve)=>{
soap.createClient(url, options, (err, client) => {
if (err){
console.log("requestForPayBP Step1",err);
resolve({result:false,entity:"errors.errorInInitPay"});
return;
}
client.bpPayRequest(args, (err, result, body) => {
if(err) {
console.log("requestForPayBP Step2",err);
resolve({result:false,entity:"errors.errorInInitPay"});
}else{
console.log("result!!!!!!",result);
const arr = result? result.return.split(",") : [100];
if (arr[0]!=="0"){
console.log("requestForPayBP Step3",result);
resolve({result:false,entity:"errors.errorInInitPay"});
}else{
const entity= {
code:arr[0], authority:arr[1],mobileNo: mobile
}
resolve({result:true,entity});
}
}
})
});
});
},
async verifyForPayZarinpal(amount, description, mobile, email) {
return new Promise(resolve => {
axios.post("https://api.zarinpal.com/pg/v4/payment/request.json", {
merchant_id: "7527a218-5adb-401f-bd3e-72eee628b25d",//"ce442962-6b0f-4094-ae81-296bc6a5b5ea",
amount: amount * 10,//rial
description,
callback_url: "https://app.optimyar.com/bankCallBack",
//callback_url: "http://localhost:3000/bankCallBack",
metadata: {
mobile,
email
}
}).then(resp => {
const {code, authority} = resp.data.data;
resolve({
result: true,
entity: {
code, authority
}
})
}).catch(err => {
resolve({
result: false,
entity: "errors.errorInInitPay"
})
})
});
}
};