use chrono::prelude::*;
use serde::{
Deserialize,
Serialize,
};
use super::users::User;
use crate::{
response::TwitchResult,
TwitchClient,
};
use serde_json::Value;
use std::{
self,
collections::HashMap,
io::Write,
};
pub fn get_post(
c: &TwitchClient,
chan_id: &str,
post_id: &str,
) -> TwitchResult<FeedPost>
{
let r =
c.get::<FeedPost>(&format!("/feed/{}/posts/{}", chan_id, post_id))?;
Ok(r)
}
pub fn get_posts<'c>(
c: &'c TwitchClient,
chan_id: &str,
) -> TwitchResult<FeedPosts<'c>>
{
let iter = FeedPosts {
client: c,
chan_id: String::from(chan_id),
cur: None,
cursor: None,
};
Ok(iter)
}
pub fn new_post(
c: &TwitchClient,
chan_id: &str,
data: &str,
) -> TwitchResult<NewFeedPostResponse>
{
let r = c.post::<NewContent, NewFeedPostResponse>(
&format!("/feed/{}/posts", chan_id),
&NewContent { content: data },
)?;
Ok(r)
}
pub fn delete_post(
c: &TwitchClient,
chan_id: &str,
post_id: &str,
) -> TwitchResult<FeedPost>
{
let r =
c.delete::<FeedPost>(&format!("/feed/{}/posts/{}", chan_id, post_id))?;
Ok(r)
}
pub fn new_post_reaction(
c: &TwitchClient,
chan_id: &str,
post_id: &str,
emote_id: &str,
) -> TwitchResult<NewReactionResponse>
{
let r = c.post::<Value, NewReactionResponse>(
&format!(
"/feed/{}/posts/{}/reactions?emote_id={}",
chan_id, post_id, emote_id
),
&Value::Null,
)?;
Ok(r)
}
pub fn delete_post_reaction(
c: &TwitchClient,
chan_id: &str,
post_id: &str,
emote_id: &str,
) -> TwitchResult<DelReactionResponse>
{
let r = c.delete::<DelReactionResponse>(&format!(
"/feed/{}/posts/{}/reactions?emote_id={}",
chan_id, post_id, emote_id
))?;
Ok(r)
}
pub fn get_comments<'c>(
c: &'c TwitchClient,
chan_id: &str,
post_id: &str,
) -> TwitchResult<FeedPostCommentIterator<'c>>
{
let iter = FeedPostCommentIterator {
client: c,
chan_id: String::from(chan_id),
post_id: String::from(post_id),
cur: None,
cursor: None,
};
Ok(iter)
}
pub fn new_comment(
c: &TwitchClient,
chan_id: &str,
post_id: &str,
data: &str,
) -> TwitchResult<FeedPostComment>
{
let r = c.post::<NewContent, FeedPostComment>(
&format!("/feed/{}/posts/{}/comments", chan_id, post_id),
&NewContent { content: data },
)?;
Ok(r)
}
pub fn delete_comment(
c: &TwitchClient,
chan_id: &str,
post_id: &str,
comment_id: &str,
) -> TwitchResult<FeedPostComment>
{
let r = c.delete::<FeedPostComment>(&format!(
"/feed/{}/posts/{}/comments/{}",
chan_id, post_id, comment_id
))?;
Ok(r)
}
pub fn new_comment_reaction(
c: &TwitchClient,
chan_id: &str,
post_id: &str,
comment_id: &str,
) -> TwitchResult<NewReactionResponse>
{
let r = c.post::<Value, NewReactionResponse>(
&format!(
"/feed/{}/posts/{}/comments/{}/reactions?emote_id=endorse",
chan_id, post_id, comment_id
),
&Value::Null,
)?;
Ok(r)
}
pub fn delete_comment_reaction(
c: &TwitchClient,
chan_id: &str,
post_id: &str,
comment_id: &str,
) -> TwitchResult<DelReactionResponse>
{
let r = c.delete::<DelReactionResponse>(&format!(
"/feed/{}/posts/{}/comments/{}/reactions?emote_id=endorse",
chan_id, post_id, comment_id
))?;
Ok(r)
}
#[derive(Deserialize, Debug)]
pub struct FeedPost {
pub body: String,
pub comments: Option<SerdeFeedPostComments>,
pub created_at: DateTime<Utc>,
pub deleted: Option<bool>,
pub embeds: Option<Vec<Value>>,
pub emotes: Option<Vec<Value>>,
pub id: String,
pub permissions: Option<FeedPostPermissions>,
pub reactions: Option<HashMap<String, Value>>,
pub user: Option<User>,
}
#[derive(Deserialize, Debug)]
pub struct FeedPostPermissions {
pub can_delete: bool,
pub can_moderate: bool,
pub can_reply: bool,
}
#[derive(Deserialize, Debug)]
pub struct FeedPostCommentPermissions {
pub can_delete: bool,
}
#[derive(Deserialize, Debug)]
pub struct FeedPostEmotes {
pub start: i32,
pub end: i32,
pub id: i64,
pub set: i64,
}
#[derive(Serialize, Debug)]
pub struct NewContent<'a> {
pub content: &'a str,
}
#[derive(Deserialize, Debug)]
pub struct NewFeedPostResponse {
pub post: FeedPost,
pub tweet: Option<String>,
}
#[derive(Deserialize, Debug)]
pub struct NewReactionResponse {
pub created_at: DateTime<Utc>,
pub emote_id: String,
pub id: String,
pub user: Option<User>,
}
#[derive(Deserialize, Debug)]
pub struct DelReactionResponse {
pub deleted: bool,
}
#[derive(Debug)]
pub struct FeedPosts<'c> {
client: &'c TwitchClient,
chan_id: String,
cur: Option<SerdeFeedPosts>,
cursor: Option<String>,
}
impl<'c> Iterator for FeedPosts<'c> {
type Item = FeedPost;
fn next(&mut self) -> Option<FeedPost> {
let url = format!("/feed/{}/posts?", &self.chan_id);
next_result_cursor!(self, &url, SerdeFeedPosts, posts)
}
}
#[derive(Deserialize, Debug)]
struct SerdeFeedPosts {
pub posts: Vec<FeedPost>,
pub _cursor: Option<String>,
}
#[derive(Debug)]
pub struct FeedPostCommentIterator<'c> {
client: &'c TwitchClient,
chan_id: String,
post_id: String,
cur: Option<SerdeFeedPostComments>,
cursor: Option<String>,
}
#[derive(Deserialize, Debug)]
pub struct FeedPostComment {
pub body: String,
pub created_at: DateTime<Utc>,
pub deleted: bool,
pub emotes: Vec<FeedPostEmotes>,
pub id: String,
pub permissions: Option<FeedPostCommentPermissions>,
pub reactions: HashMap<String, Value>,
pub user: User,
}
#[derive(Deserialize, Debug)]
pub struct SerdeFeedPostComments {
pub _total: i32,
pub _cursor: Option<String>,
pub comments: Vec<FeedPostComment>,
}
impl<'c> Iterator for FeedPostCommentIterator<'c> {
type Item = FeedPostComment;
fn next(&mut self) -> Option<FeedPostComment> {
let url = format!(
"/feed/{}/posts/{}/comments?",
&self.chan_id, &self.post_id
);
next_result_cursor!(self, &url, SerdeFeedPostComments, comments)
}
}
#[cfg(test)]
mod tests {
use crate::{
new,
tests::{
CHANID,
CLIENTID,
TOKEN,
},
};
use crate::kraken::channel_feed::{
delete_comment,
delete_comment_reaction,
delete_post,
delete_post_reaction,
get_posts,
new_comment,
new_comment_reaction,
new_post,
new_post_reaction,
};
#[test]
#[ignore]
fn post() {
let mut c = new(String::from(CLIENTID));
c.set_oauth_token(TOKEN);
match new_post(&c, CHANID, "channel_feed::tests::FeedPost") {
Ok(r) => assert_eq!(r.post.body, "channel_feed::tests::FeedPost"),
Err(r) => {
println!("{:?}", r);
assert!(false);
}
};
match get_posts(&c, CHANID) {
Ok(r) => assert!(r.count() > 0),
Err(r) => {
println!("{:?}", r);
assert!(false);
}
}
for post in get_posts(&c, CHANID).unwrap() {
match new_post_reaction(&c, CHANID, &post.id, "25") {
Ok(_r) => (),
Err(_r) => assert!(false),
}
}
for post in get_posts(&c, CHANID).unwrap() {
match post.reactions.expect("no reactions for post").get("25") {
Some(_r) => (),
None => assert!(false),
}
match delete_post_reaction(&c, CHANID, &post.id, "25") {
Ok(r) => assert!(r.deleted),
Err(r) => {
println!("{:?}", r);
assert!(false);
}
}
}
for post in get_posts(&c, CHANID).unwrap() {
assert_eq!(
post.reactions
.expect("no reactions for post")
.keys()
.count(),
0
);
}
for post in get_posts(&c, CHANID).unwrap() {
match delete_post(&c, CHANID, &post.id) {
Ok(r) => assert!(r.id == post.id && r.deleted.unwrap() == true),
Err(r) => {
println!("{:?}", r);
assert!(false);
}
}
}
match get_posts(&c, CHANID) {
Ok(r) => assert!(r.count() == 0),
Err(r) => {
println!("{:?}", r);
assert!(false);
}
}
}
#[test]
#[ignore]
fn comment() {
let mut c = new(String::from(CLIENTID));
c.set_oauth_token(TOKEN);
if let Ok(r) =
new_post(&c, CHANID, "channel_feed::tests::FeedPostComment")
{
let post = r.post;
if let Ok(comment) = new_comment(
&c,
CHANID,
&post.id,
"channel_feed::tests::FeedPostComment comment",
) {
match new_comment_reaction(&c, CHANID, &post.id, &comment.id) {
Ok(_r) => (),
Err(r) => {
println!("{:?}", r);
assert!(false);
}
};
match delete_comment_reaction(&c, CHANID, &post.id, &comment.id)
{
Ok(r) => assert_eq!(r.deleted, true),
Err(r) => {
println!("{:?}", r);
assert!(false);
}
}
match delete_comment(&c, CHANID, &post.id, &comment.id) {
Ok(r) => assert_eq!(r.deleted, true),
Err(r) => {
println!("{:?}", r);
assert!(false);
}
}
}
else {
assert!(false);
}
for post in get_posts(&c, CHANID).unwrap() {
match delete_post(&c, CHANID, &post.id) {
Ok(_r) => (),
Err(r) => {
println!("{:?}", r);
assert!(false);
}
}
}
}
else {
assert!(false);
}
}
}