Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
sss.openstar.message.MessageInBoxActor.scala Maven / Gradle / Ivy
package sss.openstar.message
import akka.actor.{Actor, ActorContext, ActorLogging, ActorRef, Props, Status}
import akka.pattern.pipe
import sss.ancillary.Guid
import sss.db.Db
import sss.openstar.chains.TxWriterActor.InternalCommit
import sss.openstar.contacts.ContactService
import sss.openstar.message.IncomingMessageProcessorUtil.defaultAsyncIncomingMessageConsumer
import sss.openstar.message.MessageDownloadActor._
import sss.openstar.message.MessageInBoxActor.{ConsumeResultsWrapper, NewPostPaywallMessageDownloaded, SaveOutgoingMessage}
import sss.openstar.message.MessagePayloadDecoder.MessagePayloadType
import sss.openstar.message.payloads.PaywalledMessage
import sss.openstar.network.MessageEventBus
import sss.openstar.tools.SendTxSupport.SendTx
import sss.openstar.util.ActorTryAndLog
import sss.openstar.{BusEvent, UniqueNodeIdentifier}
import scala.concurrent.Promise
object MessageInBoxActor {
case class SaveOutgoingMessage(from: UniqueNodeIdentifier, msg: Message, promise: Promise[Unit])
extends BusEvent
private case class ConsumeResultsWrapper(wrapped: ConsumeResults, guid: Guid)
private case class NewPostPaywallMessageDownloaded(forWho: UniqueNodeIdentifier, msg: Message)
case class InBoxAddSent(forWho: UniqueNodeIdentifier, msg: Message) extends BusEvent
case class InBoxAdd(forWho: UniqueNodeIdentifier, msg: Message) extends BusEvent
case class InBoxUpdate(forWho: UniqueNodeIdentifier, msg: Message) extends BusEvent
case class CheckedProps(p: Props, name: String)
def name(userId: UniqueNodeIdentifier): String = s"MessageInBoxActor_$userId"
def props(messageProcessor: AsyncMessageProcessor,
userId: UniqueNodeIdentifier,
watch: UtxoWatch,
personalPaywall: PaywallChargeRedeemer
)(implicit
db: Db,
events: MessageEventBus,
sendTx: SendTx,
contactService: ContactService,
outgoingMessageProcessorUtil: OutgoingMessageProcessorUtil,
utxoQuery: UtxoQuery
): CheckedProps = CheckedProps(Props(
classOf[MessageInBoxActor],
messageProcessor,
userId,
watch,
personalPaywall,
outgoingMessageProcessorUtil,
db,
events,
sendTx,
contactService,
utxoQuery
), name(userId))
def apply(props: CheckedProps)(implicit context: ActorContext): ActorRef =
context.actorOf(props.p, props.name)
}
class MessageInBoxActor(
messageProcessor: AsyncMessageProcessor,
userId: UniqueNodeIdentifier,
watch: UtxoWatch,
personalPaywall: PaywallChargeRedeemer,
outgoingMessageProcessorUtil: OutgoingMessageProcessorUtil
)(implicit
db: Db,
events: MessageEventBus,
sendTx: SendTx,
contactService: ContactService,
utxoQuery: UtxoQuery
) extends Actor
with ActorLogging
with ActorTryAndLog {
override def preStart(): Unit = {
super.preStart()
events subscribe classOf[NewMessageDownloaded]
events subscribe classOf[SaveOutgoingMessage]
}
private case class MessageTxTracker(msg: Message)
log.info("MessageInBox actor has started...")
import context.dispatcher
private val messageConsumer = (messageProcessor orElse defaultAsyncIncomingMessageConsumer).lift
private val who = userId
private implicit val inBox = MessageInBox(who)
implicit val utxoWatch = watch
private val paywalledType = MessagePayloadType.PaywalledMessageType.id.toByte
override def receive: Receive = {
case NewMessageDownloaded(`who`, m@Message(MessagePayload(`paywalledType`, _), guid, parentOpt)) =>
tryAndLog {
DecodedMessageCache(m).map {
case PaywalledMessage(from, indexOpt, payload) =>
personalPaywall(from, who)(indexOpt) map {
case None =>
//no paywall payment required
self ! NewPostPaywallMessageDownloaded(who, Message(payload, guid, parentOpt))
case Some(ledgerItemF) =>
ledgerItemF flatMap { ledgerItem =>
sendTx(ledgerItem).whenCommitted.map(_.internalCommit) map {
case _: InternalCommit =>
self ! NewPostPaywallMessageDownloaded(who, Message(payload, guid, parentOpt))
case e =>
//see if it already processed.
indexOpt.map(index => utxoQuery(index) match {
case Some(_) =>
log.warning("Error processing paywall amount {}", e)
case None =>
//try again //TODO
})
}
}
} recover { case e =>
log.warning("Error getting paywall amount {}", e)
}
}
}.getOrElse(throw new RuntimeException(s"Failed to decode message? ${m.guid}"))
case NewMessageDownloaded(`who`, m) =>
log.error(s"No non paywalled messages are allowed - this is message type ${m.msgPayload.payloadType}")
case NewPostPaywallMessageDownloaded(`who`, m @ Message(msgPayload, guid, parentGuidOpt)) =>
tryAndLog {
parentGuidOpt.foreach { pg =>
if (inBox.find(pg).isEmpty) {
log.warning(s"$who Parent $pg can't be found for $guid ? '")
}
}
DecodedMessageCache(m).map { childDecoded =>
val expandedMessage =
ExpandedMessage(
ExpandedComposite(
childDecoded,
guid,
msgPayload
),
parentGuidOpt
.flatMap(inBox.find)
.flatMap { parentMessage =>
DecodedMessageCache(parentMessage.msg)
.map(p =>
ExpandedComposite(
p,
parentMessage.msg.guid,
parentMessage.msg.msgPayload
)
)
}
)
//TODO this will result in all kinds
// of race conditions. Must rewrite.
messageConsumer(expandedMessage)
.map(_ map (ConsumeResultsWrapper(_, guid)))
.map(_ pipeTo self)
}.getOrElse(log.warning(s"Could not decode ${m.msgPayload.payloadType}"))
}
case Status.Failure(e) =>
log.warning("Failed to consume results {}", e)
case ConsumeResultsWrapper(results, guid) =>
tryAndLog {
val dedup = results.distinct
//log.warning(results.toString)
if (dedup.size != results.size) {
log.warning(s"Dedup removed ${results.size - dedup.size} elements")
log.warning(results.toString)
}
MessageProcessorUtils.defaultHandleConsumeResults(dedup)
events publish MessageProcessed(who, guid)
}
case SaveOutgoingMessage(`who`, msg, promise) =>
promise.complete(
outgoingMessageProcessorUtil.processOutgoing(who, msg)
)
}
}