Skip to content

Commit

Permalink
feat(bots): add viber bot integration (#37)
Browse files Browse the repository at this point in the history
  • Loading branch information
vincejv authored Nov 14, 2022
1 parent dba5921 commit 8de09e6
Show file tree
Hide file tree
Showing 14 changed files with 341 additions and 5 deletions.
2 changes: 2 additions & 0 deletions .github/workflows/release-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ jobs:
MSGR_API_BASE_URI=${{ secrets.MSGR_API_BASE_URI }}
TG_API_BASE_URI=${{ secrets.TG_API_BASE_URI }}
TELEGRAM_SECRET_VERIFY_TOKEN=${{ secrets.TELEGRAM_SECRET_VERIFY_TOKEN }}
VIBER_AUTH_TOKEN=${{ secrets.VIBER_AUTH_TOKEN }}
VIBER_API_BASE_URI=${{ secrets.VIBER_API_BASE_URI }}
secrets: |
MONGO_CONN_STRING=vbl_mongo_connection_string:latest
OIDC_SECRET=oidc_secret_keycloak:latest
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/release-main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,8 @@ jobs:
MSGR_API_BASE_URI=${{ secrets.MSGR_API_BASE_URI }}
TG_API_BASE_URI=${{ secrets.TG_API_BASE_URI }}
TELEGRAM_SECRET_VERIFY_TOKEN=${{ secrets.TELEGRAM_SECRET_VERIFY_TOKEN }}
VIBER_AUTH_TOKEN=${{ secrets.VIBER_AUTH_TOKEN }}
VIBER_API_BASE_URI=${{ secrets.VIBER_API_BASE_URI }}
secrets: |
MONGO_CONN_STRING=vbl_mongo_connection_string:latest
OIDC_SECRET=oidc_secret_keycloak:latest
Expand Down
5 changes: 5 additions & 0 deletions fpi-bot-api-core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@
<artifactId>java-telegram-bot-api</artifactId>
</dependency>

<dependency>
<groupId>com.abavilla</groupId>
<artifactId>fpi-viber-plugin-lib</artifactId>
</dependency>

</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
import com.abavilla.fpi.fw.config.codec.IEnumCodecProvider;
import com.abavilla.fpi.telco.ext.codec.BotSourceCodec;
import com.abavilla.fpi.telco.ext.enums.BotSource;
import com.abavilla.fpi.viber.ext.codec.MessageTypeCodec;
import com.abavilla.fpi.viber.ext.dto.Message;
import org.bson.codecs.Codec;

/**
Expand All @@ -31,10 +33,13 @@
*/
public class EnumCodecProvider implements IEnumCodecProvider {

@SuppressWarnings("unchecked")
@Override
public <T> Codec<T> getCodecProvider(Class<T> tClass) {
if (tClass == BotSource.class) {
return (Codec<T>) new BotSourceCodec();
} else if (tClass == Message.Type.class) {
return (Codec<T>) new MessageTypeCodec();
}
return null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.abavilla.fpi.bot.codec;

import com.abavilla.fpi.fw.util.MapperUtil;
import com.abavilla.fpi.viber.ext.dto.ViberUpdate;
import com.fasterxml.jackson.databind.ObjectReader;
import com.fasterxml.jackson.databind.ObjectWriter;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.eventbus.MessageCodec;
import lombok.SneakyThrows;

public class ViberUpdateEvtCodec implements MessageCodec<ViberUpdate, ViberUpdate> {

@SneakyThrows
@Override
public void encodeToWire(Buffer buffer, ViberUpdate update) {
ObjectWriter writer = MapperUtil.mapper().writerFor(ViberUpdate.class);
byte[] bytes = writer.writeValueAsBytes(update);
buffer.appendInt(bytes.length);
buffer.appendBytes(bytes);
}

@SneakyThrows
@Override
public ViberUpdate decodeFromWire(int pos, Buffer buffer) {
// My custom message starting from this *position* of buffer

// Length of JSON
int length = buffer.getInt(pos);

// Get JSON string by it`s length
// Jump 4 because getInt() == 4 bytes
pos += 4;
byte[] bytes = buffer.getBytes(pos, pos + length);

ObjectReader reader = MapperUtil.mapper().readerFor(ViberUpdate.class);
return reader.readValue(bytes);
}

@Override
public ViberUpdate transform(ViberUpdate update) {
return update;
}

@Override
public String name() {
return this.getClass().getName();
}

@Override
public byte systemCodecID() {
return -1;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@
import com.abavilla.fpi.fw.dto.IDto;
import com.abavilla.fpi.fw.dto.impl.RespDto;
import com.abavilla.fpi.fw.exceptions.FPISvcEx;
import com.abavilla.fpi.fw.util.DateUtil;
import com.abavilla.fpi.fw.util.MapperUtil;
import com.abavilla.fpi.fw.util.SigUtil;
import com.abavilla.fpi.meta.ext.dto.MetaHookEvtDto;
import com.abavilla.fpi.viber.ext.dto.ViberUpdate;
import com.pengrad.telegrambot.BotUtils;
import io.quarkus.logging.Log;
import io.smallrye.mutiny.Uni;
Expand All @@ -51,15 +53,18 @@ public class WebhookResource extends AbsBaseResource<MetaHookEvtDto, MetaMsgEvt,
* Facebook app secret, used in HMAC signature checking
*/
@ConfigProperty(name = "com.meta.facebook.app-secret")
String webhookSecret;
String metaAppSecret;

@ConfigProperty(name = "com.viber.auth-token")
String viberAuthToken;

@Path("msgr")
@POST
public Uni<Void> receiveEventFromMsgr(
@RestHeader("X-Hub-Signature-256") String signatureHdr,
String body) {
var signature = StringUtils.removeStart(signatureHdr, BotConst.HTTP_HDR_SHA256);
if (StringUtils.isNotBlank(signatureHdr) && SigUtil.validateSignature(body, webhookSecret, signature)) {
if (StringUtils.isNotBlank(signatureHdr) && SigUtil.validateSignature(body, metaAppSecret, signature)) {
return service.processWebhook(MapperUtil.readJson(body, MetaHookEvtDto.class));
} else {
Log.warn("Signature check failed: payload: " + body + " signature: " + signatureHdr);
Expand Down Expand Up @@ -88,6 +93,24 @@ public Uni<Void> receiveEventFromTelegram(
throw new FPISvcEx("Unauthorized secret token", RestResponse.StatusCode.FORBIDDEN);
}

@Path("viber")
@POST
public Uni<RespDto<IDto>> receiveEventFromViber(
@RestHeader("X-Viber-Content-Signature") String signature,
String body) {
if (StringUtils.isNotBlank(signature) && SigUtil.validateSignature(body, viberAuthToken, signature)) {
return service.processWebhook(MapperUtil.readJson(body, ViberUpdate.class))
.replaceWith(() -> {
var resp = new RespDto<>();
resp.setTimestamp(DateUtil.nowAsStr());
resp.setStatus("received");
return resp;
});
}
Log.warn("Signature check failed: payload: " + body + " signature: " + signature);
throw new FPISvcEx(StringUtils.EMPTY, Response.Status.FORBIDDEN.getStatusCode());
}

/**
* {@inheritDoc}
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*************************************************************************
* FPI Application - Abavilla *
* Copyright (C) 2022 Vince Jerald Villamora *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>.*
*************************************************************************/

package com.abavilla.fpi.bot.entity.viber;

import java.time.LocalDateTime;

import com.abavilla.fpi.fw.entity.mongo.AbsMongoItem;
import com.abavilla.fpi.viber.ext.dto.Message;
import com.abavilla.fpi.viber.ext.dto.Sender;
import io.quarkus.mongodb.panache.common.MongoEntity;
import io.quarkus.runtime.annotations.RegisterForReflection;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@NoArgsConstructor
@RegisterForReflection
@MongoEntity(collection = "viber_event")
public class ViberEvt extends AbsMongoItem {
private String event;
private LocalDateTime timestamp;
private String chatHostname;
private Long messageToken;
private Sender sender;
private Message message;
private Boolean silent;
private String userId;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*************************************************************************
* FPI Application - Abavilla *
* Copyright (C) 2022 Vince Jerald Villamora *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>.*
*************************************************************************/

package com.abavilla.fpi.bot.mapper.viber;

import java.time.LocalDateTime;
import java.time.ZoneOffset;

import com.abavilla.fpi.bot.entity.viber.ViberEvt;
import com.abavilla.fpi.fw.mapper.IDtoToEntityMapper;
import com.abavilla.fpi.fw.util.DateUtil;
import com.abavilla.fpi.msgr.ext.dto.MsgrMsgReqDto;
import com.abavilla.fpi.viber.ext.dto.ViberUpdate;
import org.mapstruct.InjectionStrategy;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingConstants;

@Mapper(componentModel = MappingConstants.ComponentModel.CDI,
injectionStrategy = InjectionStrategy.CONSTRUCTOR)
public interface ViberEvtMapper extends IDtoToEntityMapper<ViberUpdate, ViberEvt> {

@Mapping(target = "content", source = "message.text")
@Mapping(target = "recipient", source = "sender.id")
@Mapping(target = "replyTo", source = "message.trackingData")
@Mapping(target = "id", ignore = true)
@Mapping(target = "dateCreated", ignore = true)
@Mapping(target = "dateUpdated", ignore = true)
MsgrMsgReqDto createMsgReqFromUpdate(ViberUpdate update);

default LocalDateTime timestampToLdt(Long timestamp) {
if (timestamp == null) {
return null;
}
return DateUtil.fromEpoch(timestamp);
}

default Long ldtToTimestamp(LocalDateTime timestamp) {
if (timestamp == null) {
return null;
}
return timestamp.toEpochSecond(ZoneOffset.UTC);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/******************************************************************************
* FPI Application - Abavilla *
* Copyright (C) 2022 Vince Jerald Villamora *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
******************************************************************************/

package com.abavilla.fpi.bot.processor;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;

import com.abavilla.fpi.bot.codec.ViberUpdateEvtCodec;
import com.abavilla.fpi.bot.entity.viber.ViberEvt;
import com.abavilla.fpi.bot.mapper.viber.ViberEvtMapper;
import com.abavilla.fpi.bot.repo.ViberEvtRepo;
import com.abavilla.fpi.load.ext.dto.QueryDto;
import com.abavilla.fpi.msgr.ext.dto.MsgrMsgReqDto;
import com.abavilla.fpi.msgr.ext.rest.ViberReqApi;
import com.abavilla.fpi.telco.ext.enums.BotSource;
import com.abavilla.fpi.viber.ext.dto.ViberUpdate;
import io.quarkus.logging.Log;
import io.quarkus.vertx.ConsumeEvent;
import io.smallrye.mutiny.Uni;

@ApplicationScoped
public class ViberMsgEvtPcsr extends EvtPcsr<ViberUpdate, ViberReqApi, ViberEvtRepo, ViberEvt> {

@Inject
ViberEvtMapper mapper;

@ConsumeEvent(value = "viber-msg-evt", codec = ViberUpdateEvtCodec.class)
public Uni<Void> process(ViberUpdate evt) {
Log.info("Received viber update event: " + evt);
return processEvent(evt);
}

@Override
protected Uni<Void> sendMsgrMsg(MsgrMsgReqDto msgReq, String fpiUser) {
return msgrApi.sendMsg(msgReq, fpiUser).replaceWithVoid();
}

@Override
protected Uni<Void> toggleTyping(String recipient, boolean isTyping) {
// viber doesn't have an API for typing
return Uni.createFrom().voidItem();
}

@Override
protected ViberEvt mapToEntity(ViberUpdate evt) {
return mapper.mapToEntity(evt);
}

@Override
protected String getContentFromEvt(ViberUpdate evt) {
return evt.getMessage().getText();
}

@Override
protected String getEventIdForLogging(ViberUpdate evt) {
return String.valueOf(evt.getMessageToken());
}

@Override
protected QueryDto createLoadQueryFromEvt(ViberUpdate evt) {
var query = new QueryDto();
query.setQuery(evt.getMessage().getText());
query.setBotSource(getBotSource().toString());
return query;
}

@Override
protected MsgrMsgReqDto createMsgReqFromEvt(ViberUpdate evt) {
return mapper.createMsgReqFromUpdate(evt);
}

@Override
protected String getSenderFromEvt(ViberUpdate evt) {
return evt.getSender() == null ? evt.getUserId() : evt.getSender().getId();
}

@Override
public BotSource getBotSource() {
return BotSource.VIBER;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.abavilla.fpi.bot.repo;

import javax.enterprise.context.ApplicationScoped;

import com.abavilla.fpi.bot.entity.viber.ViberEvt;
import com.abavilla.fpi.fw.repo.AbsMongoRepo;

@ApplicationScoped
public class ViberEvtRepo extends AbsMongoRepo<ViberEvt> {
}
Loading

0 comments on commit 8de09e6

Please sign in to comment.