I try to create a facebook messenger bot
I tried Victor de la Cruz method,the code is below
, and here is the origin code:
https://www.codementor.io/#vcg_cruz/a-facebook-messenger-bot-m2g6wgcxb#comments-m2g6wgcxb
it works but when I send a message it generates an infinite loop of conversation
this the result screen shot
#RestController()
#RequestMapping("webhook")
public class WebHook {
private final String PAGE_TOKEN ="THIS_IS_THE_TOKEN_YOU_COPIED_BEFORE";
private final String VERIFY_TOKEN="A_SECRET_VERIFY_TOKEN";
//this is for reply messages
private final String FB_MSG_URL="https://graph.facebook.com/v2.6/me/messages?access_token="
+ PAGE_TOKEN;
//logger to watch whats happening in our bot
private final Logger logger = LoggerFactory.getLogger(WebHook.class);
private final RestTemplate template = new RestTemplate();
//This is necessary for register a webhook in facebook
#GetMapping()
#ResponseStatus(HttpStatus.OK)
public String get(#RequestParam(name = "hub.verify_token")String token,
#RequestParam(name = "hub.challenge")String challenge){
if(token!=null && !token.isEmpty() && token.equals(VERIFY_TOKEN)){
return challenge;
}else{
return "Wrong Token";
}
}
//This method reply all messages with: 'This is a test message'
#PostMapping
#ResponseStatus(HttpStatus.OK)
public void post(#RequestBody FacebookHookRequest request){
logger.info("Message from chat: {}",request);
request.getEntry().forEach(e->{
e.getMessaging().forEach(m->{
String id = m.getSender().get("id");
sendReply(id,"This is a test message");
});
});
}
private void sendReply(String id,String text){
FacebookMessageResponse response = new FacebookMessageResponse();
response.setMessage_type("text");
response.getRecipient().put("id",id);
response.getMessage().put("text",text);
HttpEntity<FacebookMessageResponse> entity = new HttpEntity<>(response);
String result = template.postForEntity(FB_MSG_URL,entity,String.class).getBody();
logger.info("Message result: {}",result);
}
}
/************************************/
#Data
#NoArgsConstructor
#AllArgsConstructor
#ToString
public class FacebookEntry implements Serializable {
private String id;
private Long time;
private List<FacebookMessaging> messaging = new ArrayList<>();
}
#Data
#NoArgsConstructor
#AllArgsConstructor
#ToString
public class FacebookHookRequest implements Serializable {
private String object;
private List<FacebookEntry> entry = new ArrayList<>();
}
#Data
#NoArgsConstructor
#AllArgsConstructor
#ToString
public class FacebookMessage implements Serializable {
private String mid;
private Long seq;
private String text;
}
#Data
#NoArgsConstructor
#AllArgsConstructor
#ToString
public class FacebookMessageResponse implements Serializable {
private String message_type;
private Map<String,String> recipient = new HashMap<>();
private Map<String,String> message = new HashMap<>();
}
#Data
#NoArgsConstructor
#AllArgsConstructor
#ToString
public class FacebookMessaging implements Serializable {
private Map<String,String> sender;
private Map<String,String> recipient;
private Long timestamp;
private FacebookMessage message;
}
You're responding to yourself I assume. Similar to how the Discord API works, your post method is being called when you receive your own message. You most likely want to check if the message author is from yourself.
Related
This is websocketHandler
#Service
#Slf4j
public class ChatWebSocketHandler implements WebSocketHandler {
private final Sinks.Many<Event> chatMessageSink = Sinks.many().multicast().directBestEffort();
private final Flux<Event> chatMessageFlux = chatMessageSink.asFlux();
private final ObjectMapper objectMapper = new ObjectMapper();
public Mono<Void> handle(WebSocketSession session) {
Flux<WebSocketMessage> sendMessage = chatMessageFlux
.map(this::toString)
.map(session::textMessage);
Mono<Void> output = session.send(sendMessage);
Mono<Void> input = session
.receive()
.map(WebSocketMessage::getPayloadAsText)
.doOnNext(event -> {
chatMessageSink.tryEmitNext(toEvent(event));
})
.map(this::toEvent)
.then();
return Mono.zip(input, output).then();
}
#SneakyThrows
private Event toEvent(String message) {
return objectMapper.readValue(message, Event.class);
}
#SneakyThrows
private String toString(Event event) {
return objectMapper.writeValueAsString(event);
}
}
This is DTO.(mapping data from client)
#Setter
#Getter
#Builder
#NoArgsConstructor
#AllArgsConstructor
#ToString
public class Event {
private int id;
private String content;
private String sender;
}
This code is working normally.
But all clients are getting messages.
I would like to send message only to specific client like members of chat room.
help me.
I have created two microservices - Microservice 1 and Microservice 2.
Microservice 2 - Returns List of TrainDetailsToReturn(object) running on a specific date and between two cities.
Microservice 1 - Consumes Microservice 2 and returns the same List of TrainDetailsToReturn.
From Microservice 2
Controller -
#RestController
#RequestMapping("/api/v1/")
public class TrainDetailsController {
#Autowired
private TrainServices trainServices;
#PostMapping("get-train-details")
public ListOfTrainDetailsToReturn trainDetails(#RequestBody AvailableTrainDTO availableTrainDTO) throws Exception {
return trainServices.getTrainDetails(availableTrainDTO);
}
Class - ListOfTrainDetailsToReturn
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.List;
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
public class ListOfTrainDetailsToReturn {
List<TrainDetailsToReturn> list;
}
Class - TrainDetailsToReturn
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode
public class TrainDetailsToReturn {
private long trainId;
private String trainName;
private String source;
private int sourceDepartureTime;
private String destination;
private int destinationArrivalTime;
private int fare;
}
Class - AvailableTrainDTO
#Getter
#Setter
#AllArgsConstructor
#EqualsAndHashCode
#NoArgsConstructor
public class AvailableTrainDTO {
private long source_id;
private long destination_id;
private String date;
}
Microservice 1
Controller-
#RestController
#RequestMapping("/getavailabletrains")
public class TrainController {
#Autowired
private RestTemplate restTemplate;
//we take two stations and date as input and get a list of trains along with their details from the train-details
//microservice
#GetMapping
public String betweenCities(){
return "Enter source and destination along with date ";
}
#PostMapping
public ListOfTrainDetailsToReturn availableTrains(#RequestBody AvailableTrainDTO availableTrainDTO){
ListOfTrainDetailsToReturn list = restTemplate
.postForObject("http://localhost:8080/api/v1/get-train-details" ,
availableTrainDTO ,
ListOfTrainDetailsToReturn.class );
return list;
}
}
Class - ListOfTrainDetailsToReturn
import lombok.*;
import java.util.List;
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
public class ListOfTrainDetailsToReturn {
List<TrainDetailsToReturn> list;
public void setList(List<TrainDetailsToReturn> list) {
this.list = list;
}
public List<TrainDetailsToReturn> get
}
Class - TrainDetailsToReturn
import lombok.*;
#Getter
#Setter
#NoArgsConstructor
#AllArgsConstructor
#EqualsAndHashCode
public class TrainDetailsToReturn {
private long trainId;
private String trainName;
private String source;
private int sourceDepartureTime;
private String destination;
private int destinationArrivalTime;
private int fare;
}
Response from Microservice 2 to Post Request from Postman -
Request
{
"source_id":"100",
"destination_id":"140",
"date": "05/09/2022"
}
Response
{
"list": [
{
"trainId": 12018,
"trainName": "Dehradun Shatabdi Express",
"source": "Saharanpur",
"sourceDepartureTime": 1015,
"destination": "Bangalore",
"destinationArrivalTime": 1800,
"fare": 50
}
]
}
Response from Microservice to Post Request from Postman -
Request
{
"source_id":"100",
"destination_id":"140",
"date": "05/09/2022"
}
Response
{
"list": []
}
I have watched a few Youtube videos but can't really find a solution.
Before trying to answer your question, I suggest you to read this post on how to name your REST resources : https://restfulapi.net/resource-naming/ .
I guess, some PostMapping you did should be a GetMapping instead (#PostMapping("get-train-details")).
Last things, I suggest you to look at feign library if you don't know it. You could replace this old RestTemplate :) https://www.baeldung.com/intro-to-feign
So for your issue, have you try to put a breakpoint on your microservice 2 when microservice 1 calling it ? To see what do you have on input/output.
#PostMapping("get-train-details")
public ListOfTrainDetailsToReturn trainDetails(#RequestBody AvailableTrainDTO availableTrainDTO) throws Exception {
return trainServices.getTrainDetails(availableTrainDTO); // breakpoint here
}
Could you post the full requests from your postman ?
I am currently setting up a Rest API server using Spring Boot (v2.5.5), Spring Data Couchbase (v4.2.5) and Couchbase (v6.6.1).
I get a really strange behavior when requesting
count() -> 0
findAll() -> []
Whereas
findById() is returning a result.
My entity:
{"mkey": { "keyContent": "AA", "mkeyStatus": "L" }, "sequences": [ { "direction": "B", "loc1Value": "NCE", "loc2Value": "NYC" } ] }
#Document #Data #AllArgsConstructor #NoArgsConstructor #EqualsAndHashCode public class AirlineProfile {
#Id private String id;
#Field private MKey mkey;
#Field private List<Sequence> sequences;
#EqualsAndHashCode #AllArgsConstructor #NoArgsConstructor #Data static class MKey {
#Field private String keyContent;
#Field private String mkeyStatus;
}
#EqualsAndHashCode #AllArgsConstructor #NoArgsConstructor #Data static class Sequence {
#Field private String loc1Value;
#Field private String loc2Value;
#Field private String direction;
}
}
My repository is extending the CrudRepository.
public interface AirlineProfileRepository extends CrudRepository<AirlineProfile, String> {}
While my Service is the following:
#Service #Qualifier("AirlineProfileServiceImpl") public class AirlineProfileServiceImpl
implements AirlineProfileService {
#Autowired private AirlineProfileRepository airlineProfileRepository;
#Override
public long count() {
return airlineProfileRepository.count();
}
#Override
public List<AirlineProfile> findAll() {
List<AirlineProfile> airlineProfiles = new ArrayList<>();
for (AirlineProfile airlineProfile : airlineProfileRepository.findAll()) {
airlineProfiles.add(airlineProfile);
}
return airlineProfiles;
}
#Override public AirlineProfile findById(String id) {
return airlineProfileRepository.findById(id).orElse(null);
}
}
And my controller the following:
#RestController #RequestMapping("/api") public class AirlineProfileController {
#Autowired AirlineProfileService airlineProfileService;
#GetMapping("/airlineprofile/count") public long count() {
System.out.println("Count");
return airlineProfileService.count();
}
#GetMapping("/airlineprofile/all") public List<AirlineProfile> getAllAirlineProfiles() {
System.out.println("Get all AirlineProfile");
return airlineProfileService.findAll();
}
#GetMapping("/airlineprofile/id={id}") public AirlineProfile getAirlineProfileById(#PathVariable String id) {
System.out.println("Get AirlineProfile for id = " + id);
return airlineProfileService.findById(id);
}
}
I do not know if I missed something at Server or Couchbase side ... :(
Thank you for your help!
Ok, found that:
public interface AirlineProfileRepository extends CrudRepository<AirlineProfile, String> {
#Query("#{#n1ql.selectEntity}")
List<AirlineProfile> findAll();
}
Is working ...
So, I am questioning myself about the usability of findAll() ...
I have a Object that I would like Jackson to serialize like this...
<AccountsResponse>
<accounts>
<account/>
<account>
<userId>user</userId>
...
</account>
</accounts>
</AccountsResponse>
To try this I create the following class...
#Getter
#Setter
#ToString
#JsonInclude(JsonInclude.Include.NON_EMPTY)
public class Payload {
#JacksonXmlProperty(localName = "errormessage")
private String errorMessage;
}
#Getter
#Setter
#ToString
public class AccountsResponse extends Payload{
#JsonIgnore
private static Logger LOGGER = LogManager.getLogger(AccountsResponse.class);
#JacksonXmlProperty(localName = "accounts")
private List<Account> accounts = Lists.newArrayList();
public static AccountsResponse mapFromResultSet(ResultSet rs)
throws SQLException
{
AccountsResponse response = new AccountsResponse();
do {
Account acct = Account.mapFromResultSet(rs);
response.getAccounts().add(acct);
} while (rs.next());
return response;
}
public String toXml() throws JsonProcessingException {
ObjectMapper mapper = new XmlMapper();
return mapper.writeValueAsString(this);
}
}
#Getter
#Setter
#JsonInclude(JsonInclude.Include.NON_NULL)
public class Account extends ResultSetParser{
...
}
But when I serialize I get...
<AccountsResponse>
<accounts>
<accounts/>
<accounts>
<userId>user</userId>
...
</accounts>
</accounts>
</AccountsResponse>
As you can see the problem here is the child tags should be account but in fact are accounts. I tried hacking around with the localname but can't find the right mixture of VooDoo. What am I doing wrong?
I would change annotations on account list in AccountsResponse:
public class AccountsResponse extends Payload{
#JacksonXmlElementWrapper(localName = "accounts")
#JacksonXmlProperty(localName = "account")
private List<Account> accounts = Lists.newArrayList();
}
I'm trying to map source object which property is set to null to destination object of which this property is set to another object.
Expected result would be that property of destination object will be null after mapping. Instead of that, this property is set to an object and all of its properties are set to null.
Here is an example:
public class ModelMapperTest {
public static void main(String[] args) {
ModelMapper modelMapper = new ModelMapper();
User user = new User();
user.setStatus(null);
StatusDto statusDto = new StatusDto();
statusDto.setId(1);
statusDto.setName("Active");
UserDto userDto = new UserDto();
userDto.setStatus(statusDto);
// user.status=null, userDto.status=StatusDto(id=1, name="Active")
modelMapper.map(user, userDto);
System.out.println("user = " + user);
System.out.println("userDto = " + userDto);
}
#Getter
#Setter
#ToString
public static class User {
private Status status;
}
#Getter
#Setter
#ToString
public static class Status {
private Integer id;
private String name;
}
#Getter
#Setter
#ToString
public static class UserDto {
private StatusDto status;
}
#Getter
#Setter
#ToString
public static class StatusDto {
private Integer id;
private String name;
}
}
Output:
user = ModelMapperTest.User(status=null)
userDto = ModelMapperTest.UserDto(status=ModelMapperTest.StatusDto(id=null, name=null))
Is it possible to somehow configure model mapper to sets UserDto.status to null?
I know this is an older question and you seem to have moved on to a different library, but I had the same problem recently and came up with this solution (building on your example):
Converter<?, ?> preserveNullConverter = (context) ->
context.getSource() == null
? null
: modelMapper.map(context.getSource(), context.getDestinationType());
modelMapper.createTypeMap(User.class, UserDto.class)
.addMappings(mapper -> mapper.using(preserveNullConverter).map(User::getStatus, UserDto::setStatus));
It's not ideal because the .addMappings part needs to be done for every property where the issue occurs, but at least the Converter can be reused.