페이스북용 챗봇 개발중에 php로 응답 보내는 부분 구현에 어려움을 겪어 정리 차원으로 올림

페이스북의 node.js 샘플 처럼 전체 파라미터를 json_encode해서 보내면 아래와 같은 에러가 리턴된다

{
  "error": {
    "message": "(#100) The parameter recipient is required",
    "type": "OAuthException",
    "code": 100,
    "fbtrace_id": "CdhFkKgAL19"
  }
}또는 형태에 따라

{
  "error": {
    "message": "(#100) param recipient must be non-empty.",
    "type": "OAuthException",
    "code": 100,
    "fbtrace_id": "BDoRMk2PNZU"
  }
}



아래 주석 부분 주의해서 다음과 같이 하면 정상 송신이 가능하다.
$messageData = array('recipient' => array('id' => $recipientId)
              , 'message' => array('text' => $messageText, 'metadata' => 'DEVELOPER_DEFINED_METADATA'));

$params = array(
        'recipient' => json_encode($messageData['recipient']),
        'message' => json_encode($messageData['message'])
    );

    $curl = curl_init('https://graph.facebook.com/v2.6/me/messages?access_token=your_token');
    curl_setopt($curl, CURLOPT_HTTPHEADER, array('Content-Type: application/x-www-form-urlencoded'));
    curl_setopt($curl, CURLOPT_POST, true);
    curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);  // 이게 없으면 통신 자체가 안된다(에러 60)
    curl_setopt($curl, CURLOPT_POSTFIELDS, http_build_query($params));  // 전체 json이 아니라 파라미터의 값만 json이어야 한다. node.js 예제에서는 content-type도 application/json이고 파라미터도 전체 json 데이타 이다. 그 코드가 정상 작동하는지 모르겠지만 php는 위와 같이 해야 정상작동

    $json_response = curl_exec($curl);
    $status = curl_getinfo($curl, CURLINFO_HTTP_CODE);

    curl_close($curl);

    $response = json_decode($json_response, true);




한글 메시지 전송을 위해서는 파일을 utf-8로 저장해야 한다  

Posted by incree

2016/11/11 21:53 2016/11/11 21:53
, , , ,
Response
0 Trackbacks , 0 Comments
RSS :
http://www.incree.com/tc/incree/rss/response/370

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

구글 클라우드 프린트를 이용해서 웹에 있는 파일(pdf) 인쇄하기

 전체 소스 : GoogleCloudPrintExample.zip




Android Integration
https://developers.google.com/cloud-print/docs/android

Posted by incree

2016/09/17 22:34 2016/09/17 22:34
,
Response
0 Trackbacks , 0 Comments
RSS :
http://www.incree.com/tc/incree/rss/response/369

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

Custom ListView로 AlertDialog 띄우기


사용자 삽입 이미지

사용자 삽입 이미지

사용자 삽입 이미지


AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.incree.customalertdialog">

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@android:style/Theme.NoTitleBar">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>

<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>

</manifest>


MainActivity.java
package com.incree.customalertdialog;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}

public void btnOpenPopupClickHandler(View view) {
final List<Product> items = new ArrayList<Product>();
items.add(new Product(1, "와퍼세트", "와퍼 단품+프렌치프라이R+콜라R", 8000, R.drawable.product_1));
items.add(new Product(2, "너겟킹10조각", "담백한 닭가슴살로 만든 바삭한 너켓킹", 5100, R.drawable.product_2));
items.add(new Product(3, "머쉬룸스테이크버거세트", "머쉬룸스테이크버거 단품+프렌치프라이R+콜라R", 9600, R.drawable.product_3));
items.add(new Product(4, "트리오팩", "통새우와퍼+와퍼+와퍼주니어+프렌치프라이(R)+치즈프라이+너겟킹4조각+어니언링+콜라(R)3", 24500, R.drawable.product_4));
items.add(new Product(5, "롱킹", "쇠고기 (호주산과 뉴질랜드산 섞음)", 6100, R.drawable.product_5));
final ProductListAdapter adapter = new ProductListAdapter(MainActivity.this, items);
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this)
.setAdapter(adapter, null)
.setTitle("상품선택")
.setPositiveButton("선택구매", new DialogInterface.OnClickListener() { // 버튼은 테마에 따라서 모양이 다르게 모임
public void onClick(DialogInterface dialog, int whichButton) {
for(Product product : items) {
if (product.isSelected()) {
Log.d("MainActivity", product.getProductTitle() + " 선택");
}
}
}
})
.setNegativeButton("취소", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
Log.d("MainActivity", "취소 터치");
}
});

AlertDialog alertDialog = builder.create();
final ListView listView = alertDialog.getListView();
listView.setAdapter(adapter);
//listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); // 여러 상품 선택을 위해 - 이 형태에서는 필요 없다
listView.setDivider(new ColorDrawable(Color.LTGRAY));
listView.setDividerHeight(1);
listView.setFocusable(false); // false를 해줘야 row touch event 가능
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
CheckBox cbGoLargeChecked = (CheckBox)view.findViewById(R.id.productChecked);
Product tmpVo = items.get(position);
tmpVo.setSelected(!tmpVo.isSelected());
cbGoLargeChecked.setChecked(tmpVo.isSelected());
}
});

alertDialog.show();
}
}


class ProductListAdapter extends BaseAdapter
{
private Activity activity;
private List<Product> dataList;

public ProductListAdapter(Activity activity, List<Product> dataList) {
this.activity = activity;
this.dataList = dataList;
}

@Override
public int getCount()
{
return dataList.size();
}

@Override
public Object getItem(int position)
{
return dataList.get(position);
}

@Override
public long getItemId(int position)
{
return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent)
{
View view = convertView;
if(view == null) {
LayoutInflater inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = inflater.inflate(R.layout.product_dialog_list_row, null);
}

final Product data = dataList.get(position);

ImageView ivProductImage = (ImageView)view.findViewById(R.id.productImage);
final CheckBox cbProductChecked = (CheckBox)view.findViewById(R.id.productChecked);
TextView tvProductTitle = (TextView)view.findViewById(R.id.productTitle);
TextView tvProductDesc = (TextView)view.findViewById(R.id.productDesc);

cbProductChecked.setFocusable(false); // false를 해줘야 row touch event 가능

cbProductChecked.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
data.setSelected(cbProductChecked.isChecked());
}
});

ivProductImage.setImageDrawable(activity.getResources().getDrawable(data.getProductImageResource()));
tvProductTitle.setText(data.getProductTitle());
tvProductDesc.setText(data.getProductDesc());
cbProductChecked.setText(data.getProductPrice() + "원");
cbProductChecked.setChecked(data.isSelected());

return view;
}
}


activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.incree.customalertdialog.MainActivity">

<Button
android:id="@+id/btnOpenPopup"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="btnOpenPopupClickHandler"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:text="상품 팝업 열기"/>
</RelativeLayout>





product_dialog_list_row.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">

<ImageView
android:id="@+id/productImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="3dp"
android:layout_centerVertical="true"/>

<TextView
android:id="@+id/productTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="3dp"
android:layout_marginTop="5dp"
android:layout_toRightOf="@id/productImage"
android:layout_alignParentTop="true"
android:hint="상품명"
android:textColor="#252525"
android:textSize="16sp"
android:textStyle="bold"/>

<CheckBox
android:id="@+id/productChecked"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="3dp"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layoutDirection="rtl"
android:textColor="#252525"
/>

<TextView
android:id="@+id/productDesc"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="3dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:layout_toRightOf="@id/productImage"
android:layout_toLeftOf="@id/productChecked"
android:layout_below="@id/productTitle"
android:hint="상품 설명"
android:textColor="#252525"
android:textSize="12sp"/>
</RelativeLayout>

 전체 소스 : CustomAlertDialog.zip


Posted by incree

2016/09/10 09:08 2016/09/10 09:08
, ,
Response
0 Trackbacks , 0 Comments
RSS :
http://www.incree.com/tc/incree/rss/response/368

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

안드로이드 웹뷰 회전시 상태 유지

android:configChanges="orientation|keyboardHidden|screenSize"

screenSize 는 Android 3.2(API level 13) or higher device


Posted by incree

2016/06/01 22:16 2016/06/01 22:16
,
Response
0 Trackbacks , 0 Comments
RSS :
http://www.incree.com/tc/incree/rss/response/367

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

Swift Cookie sharing between multiple WKWebViews

https://github.com/ShingoFukuyama/WKWebViewTips

self.processPool = [[WKProcessPool alloc] init];

WKWebViewConfiguration *configuration1 = [[WKWebViewConfiguration alloc] init];
configuration1.processPool = self.processPool;
WKWebView *webView1 = [[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration1];
...
WKWebViewConfiguration *configuration2 = [[WKWebViewConfiguration alloc] init];
configuration2.processPool = self.processPool;
WKWebView *webView2 = [[WKWebView alloc] initWithFrame:CGRectZero configuration:configuration2];

Posted by incree

2016/04/21 20:54 2016/04/21 20:54
, , , , ,
Response
0 Trackbacks , 0 Comments
RSS :
http://www.incree.com/tc/incree/rss/response/366

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다




http://www.facebook.com/dialog/pagetab?app_id=YOUR_APP_ID&next=http://facebook.com

문서화되지 않은 방법: 또는 다음 URL을 호출하여도 탭 추가가 가능하다.

https://www.facebook.com/add.php?api_key=YOUR_APP_ID&pages

Updated(2012/03/16) 최근 페이스북이 앱페이지(앱 커뮤니티 페이지)의 관리자 메뉴 속에 앱을 페이지에 추가하기 메뉴를 추가했다. 앱페이지를 만들었다면 이 메뉴 기능을 이용해도 된다. 자세한 내용


Add Page Tab Dialog

https://developers.facebook.com/docs/pages/page-tab-dialog
FB.ui({
  method: 'pagetab',
  redirect_uri: 'YOUR_URL'
}, function(response){});
https://www.facebook.com/dialog/pagetab?
  app_id=YOUR_APP_ID
  &redirect_uri=YOUR_URL

 https://developers.facebook.com/docs/pages/tabs

FB.ui({
  method: 'pagetab',
  redirect_uri: 'YOUR_URL'
}, function(response){});
https://www.facebook.com/dialog/pagetab?
  app_id=YOUR_APP_ID
  &redirect_uri=YOUR_URL

Posted by incree

2015/08/21 23:52 2015/08/21 23:52
, , , , ,
Response
0 Trackbacks , 0 Comments
RSS :
http://www.incree.com/tc/incree/rss/response/365

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

[MySQL] MySQL server has gone away error


몇일전 운영하는 사이트에서 이 에러가 발생해서 정리

MySQL 공식사이트에서 이 에러에 대한 페이지를 보면(https://dev.mysql.com/doc/refman/5.0/en/gone-away.html)
이 에러는 증상에 따라 에러 코드가 두가지로 나뉜다.

2006 : CR_SERVER_GONE_ERROR : The client couldn't send a question to the server.
2013 : CR_SERVER_LOST : The client didn't get an error when writing to the server, but it didn't get a full answer (or any answer) to the question.


원인을 크게 세가지로 나눌수 있는데

1. 어떤 이유에서든 MySQL 서버와 연결이 끊어진 경우(wait_timeout 등)
 - 재연결하거나 끊어진 원인을 파악해서 끊어지지 않게 처리

2. 쿼리가 너무 큰 경우( max_allowed_packet variable 에 설정된 값보다 큰 쿼리를 실행하면 발생)
 - variable 값을 키우거나 쿼리를 작게 만든다

3. --skip-networking 옵션을 사용하여 MySQL을 시작한 경우
 - 이 옵션을 빼고 시작한다.


Posted by incree

2015/07/29 12:30 2015/07/29 12:30
Response
0 Trackbacks , 0 Comments
RSS :
http://www.incree.com/tc/incree/rss/response/364

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다


http://graph.facebook.com/facebookid/picture?type=type_value
facebookid : 원하는 회원의 페이스북 id
type_value : large, normal, small, square 중 하나

ex)
http://graph.facebook.com/100006497919491/picture?type=large

Posted by incree

2014/11/22 00:57 2014/11/22 00:57
, , , ,
Response
0 Trackbacks , 0 Comments
RSS :
http://www.incree.com/tc/incree/rss/response/362

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

[SNS] 페이스북 공유 결과 리턴받기

facebook share dialog response

페이스북에 공유하기 후 결과를 리턴받고 성공/실패에 따라서 처리를 다르게 할때

아래와 같이 FB.ui를 사용하면 된다.

<script type="text/javascript">


function facebookShare() {
FB.ui(
 {
   method: 'share',
   href: 'http://dev.mixedsocial.co.kr/fb_share_test.html',
 },
 function(response) {
  console.log(response);
   if (response && !response.error_code) {
     alert('Posting completed.');
   } else {
     alert('Error while posting.');
   }
 }
);
}

</script>


<div id="fb-root"></div>
<script>(function(d, s, id) {
  var js, fjs = d.getElementsByTagName(s)[0];
  if (d.getElementById(id)) return;
  js = d.createElement(s); js.id = id;
  js.src = "//connect.facebook.net/ko_KR/sdk.js#xfbml=1&appId=370268463107731&version=v2.0";
  fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));</script>


<div id="wrap">
<a title="페이스북 공유하기" class="share-button popup" href="#" onclick="facebookShare(); return false;">
<span>페이스북 공유하기</span>
</a>
</div>




페이스북 메뉴얼 : https://developers.facebook.com/docs/javascript/reference/FB.ui

Posted by incree

2014/10/22 19:10 2014/10/22 19:10
, , ,
Response
0 Trackbacks , 0 Comments
RSS :
http://www.incree.com/tc/incree/rss/response/361

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다


Android Facebook 연동 작업시에 발생하는 에러 처리

1. Failed to find provider info for com.facebook.wakizashi.provider.PlatformProvider
1) Menifest에 INTERNET 퍼미션 추가
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
2)

Posted by incree

2014/10/21 03:33 2014/10/21 03:33
,
Response
0 Trackbacks , 0 Comments
RSS :
http://www.incree.com/tc/incree/rss/response/360

Trackback URL : 이 글에는 트랙백을 보낼 수 없습니다

« Previous : 1 : 2 : 3 : 4 : 5 : 6 : ... 13 : Next »